Decomposition of potato yield gap in the Andean north of Peru

A crop modeling approach

Background

This repository is designed to provide comprehensive documentation of the R code used for the analysis of yield gap decomposition, integrating both crop modeling and stochastic frontier analysis.

Libraries and extra R-files

The following libraries were used:

# libraries
library(nasapower)
library(meteor)
library(lubridate)
library(Metrics)
library(dplyr)
library(tidyr)
library(minpack.lm)
library(ggplot2)

Additionally, extra R-files need to be loaded.

# extra R-files
load(url("https://github.com/jninanya/solanumR/raw/refs/heads/main/CropParamsList.Rdata"))
source("https://raw.githubusercontent.com/jninanya/solanumR/refs/heads/main/thermalTime.R")
source("https://raw.githubusercontent.com/jninanya/solanumR/refs/heads/main/SolanumModel.R")

Field experiment and data collection

A field experiment was conducted to determine the potential yield of the five most commonly grown potato varieties in Chugay, La Libertad, Peru: Amarillis, Bretaña, Huevo de Indio, Poderosa, and Yungay. These varieties were managed under optimal growing conditions to assess their yield potential. The trial took place from 24th October 2023 to 18th April 2024, providing insights into how these varieties perform in a local context under “ideal” conditions.

Figura 1: Field experiment to determine potential yield of the most common potato varieties in Chugay Distric, La-Libertad region, Peru.
Figura 1: Field experiment to determine potential yield of the most common potato varieties in Chugay Distric, La-Libertad region, Peru.

A total of five biomass evaluations were carried out, where the plant organs (leaf, stem, root, and tubers) were separated, every two weeks from the tuberization stage to harvest. Additionally, fifteen canopy cover evaluations were conducted from plant emergence to senescence, providing detailed data on the growth and development of the five potato varieties. The dataset related to this field experiment can be downloaded at https://doi.org/10.21223/JPA3NZ.

Biomass data

Lets see the harvest index, i.e., the ratio of tuber biomass over total biomass. Click on code to see the R chunk code for the canopy biomass data.

#---------------------------------------------------------------
# SUMMARY OF BIOMASS DATA
#---------------------------------------------------------------
BD <- read.csv("https://github.com/jninanya/Potato_Yield_Gap/raw/refs/heads/main/Data/biomass_dataset.csv")
#head(BD)

# Constant to convert kg/plant to t/ha
k = (1/1000)*(1/(0.3*1))*(10000/1)*0.001

# Matter concentration of <organ> (MCX)
BD$MCL <- BD$sDLM/BD$sFLM      # L = leaf
BD$MCS <- BD$sDSM/BD$sFSM      # S = stem
BD$MCR <- BD$sDRM/BD$sFRM      # R = root
BD$MCT <- BD$sDTM/BD$sFTM      # T = tuber
BD$DMC <- BD$MCT                 

# Dry <organ> matter (DXM)
BD$DLM <- BD$FLM*BD$MCL
BD$DSM <- BD$FSM*BD$MCS
BD$DRM <- BD$FRM*BD$MCR
BD$DTM <- BD$FTM*BD$MCT

# Fresh <organ> yield (FXY)
BD$FLY <- BD$FLM*k
BD$FSY <- BD$FSM*k
BD$FRY <- BD$FRM*k
BD$FTY <- BD$FTM*k               

# Dry <organ> yield (DXY)
BD$DLY <- BD$DLM*k
BD$DSY <- BD$DSM*k
BD$DRY <- BD$DRM*k
BD$DTY <- BD$DTM*k

# Total dry matter (TDM) and harvest index (HI)
BD$TDM <- BD$DLY + BD$DSY + BD$DRY + BD$DTY
BD$HI <- BD$DTY/BD$TDM

# Summary: mean and standard error for HI
smrBD <- BD %>%
  group_by(CODE, EVAL, DAP) %>%
  summarise(DLY = mean(DLY, na.rm = TRUE),
            DSY = mean(DSY, na.rm = TRUE),
            DRY = mean(DRY, na.rm = TRUE),
            DTY = mean(DTY, na.rm = TRUE),
            FTY = mean(FTY, na.rm = TRUE),
            TDM = mean(TDM, na.rm = TRUE),
            DMC = mean(DMC, na.rm = TRUE),
            N = sum(!is.na(HI)),
            nd = n(),
            HI_mean = mean(HI, na.rm = TRUE),
            HI_se = sd(HI, na.rm = TRUE)/sqrt(N)
    )
smrBD <- as.data.frame(smrBD)

outBD <- smrBD[, c("CODE","DAP","HI_mean")] %>%
  pivot_wider(names_from = CODE, values_from = HI_mean)
outBD

outTDM <- smrBD[, c("CODE","DAP","TDM")] %>%
  pivot_wider(names_from = CODE, values_from = TDM)
outTDM <- as.data.frame(outTDM)

outDMC <- smrBD[, c("CODE","DAP","DMC")] %>%
  pivot_wider(names_from = CODE, values_from = DMC)
outDMC <- as.data.frame(outDMC)

outFTY <- smrBD[, c("CODE","DAP","FTY")] %>%
  pivot_wider(names_from = CODE, values_from = FTY)
outFTY <- as.data.frame(outFTY)

# Data frame for each variety
AMA <- smrBD[smrBD$CODE=="AMA",]
BRE <- smrBD[smrBD$CODE=="BRE",]
HUE <- smrBD[smrBD$CODE=="HUE",]
POD <- smrBD[smrBD$CODE=="POD",]
YUN <- smrBD[smrBD$CODE=="YUN",]

# General plot settings
par(oma    = c(4.5, 4.5, 0.5, 3),  # general margins
    mfrow  = c(1, 5),                # number of sub-figures
    mar    = c(0, 0, 0, 0),          # margins per sub-figure
    family = "serif",                # text family
    lwd    = 1.0,                    # line width
    las    = 1,                      # style of axis labels  
    pch    = 19,                     # plotting points
    cex    = 0.8
)

# Color and y-axis limits
pC <- c("blue","yellow","green","cyan","red") 
yL <- c(0,1) 

# Plot for Amarilis
with(AMA, plot(x=DAP, y=HI_mean, ylim=yL, col=pC[1], axes=FALSE, xlab="", ylab=""))
with(AMA, lines(x=DAP, y=HI_mean, lty=2, col=pC[1]))
box(); axis(2); axis(4, labels=FALSE);axis(1)
mtext(side=2, "Harvest Index", line=3, las=0)

# Plot for Bretana
with(BRE, plot(x=DAP, y=HI_mean, ylim=yL, col=pC[2], axes=FALSE, xlab="", ylab=""))
with(BRE, lines(x=DAP, y=HI_mean, lty=2, col=pC[2]))
box(); axis(2,labels=FALSE); axis(4, labels=FALSE);axis(1)

# Plot for Huevo de Indio
with(HUE, plot(x=DAP, y=HI_mean, ylim=yL, col=pC[3], axes=FALSE, xlab="", ylab=""))
with(HUE, lines(x=DAP, y=HI_mean, lty=2, col=pC[3]))
box(); axis(2,labels=FALSE); axis(4, labels=FALSE);axis(1)
mtext(side=1, "Days after planting", line=3)

# Plot for Poderosa
with(POD, plot(x=DAP, y=HI_mean, ylim=yL, col=pC[4], axes=FALSE, xlab="", ylab=""))
with(POD, lines(x=DAP, y=HI_mean, lty=2, col=pC[4]))
box(); axis(2,labels=FALSE); axis(4, labels=FALSE);axis(1)

# Plot for Yungay
with(YUN, plot(x=DAP, y=HI_mean, ylim=yL, col=pC[5], axes=FALSE, xlab="", ylab=""))
with(YUN, lines(x=DAP, y=HI_mean, lty=2, col=pC[5]))
box(); axis(2,labels=FALSE); axis(4);axis(1)

## # A tibble: 6 × 6
##     DAP    AMA    BRE    HUE    POD    YUN
##   <int>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>
## 1    94  0.596  0.208  0.277  0.148  0.245
## 2   101  0.668 NA     NA     NA     NA    
## 3   113  0.727  0.376  0.477  0.405  0.434
## 4   136  0.834  0.599  0.706  0.677  0.674
## 5   154  0.872  0.700  0.803  0.718  0.766
## 6   178 NA      0.729  0.854  0.790  0.856

Canopy cover data

Click on code to see the R chunk code for the canopy cover data.

#---------------------------------------------------------------
# SUMMARY OF CANOPY COVER DATA
#---------------------------------------------------------------
CD <- read.csv("https://github.com/jninanya/Potato_Yield_Gap/raw/refs/heads/main/Data/canopy_cover_dataset.csv")
#head(CD)

# Summary by variety and evaluation
smrCD <- CD %>%
  group_by(CODE, DAP) %>%
  summarise(N = sum(!is.na(CC)),
            nd = n(),
            CC_mean = mean(CC, na.rm = TRUE),
            CC_se = sd(CC, na.rm = TRUE)/sqrt(N),
  )

smrCD <- as.data.frame(smrCD)

# Data frame per variety
outCD <- smrCD[, c("CODE","DAP","CC_mean")] %>%
  pivot_wider(names_from = CODE, values_from = CC_mean)
outCD

# Data frame for each variety
AMA <- smrCD[smrCD$CODE=="AMA",]
BRE <- smrCD[smrCD$CODE=="BRE",]
HUE <- smrCD[smrCD$CODE=="HUE",]
POD <- smrCD[smrCD$CODE=="POD",]
YUN <- smrCD[smrCD$CODE=="YUN",]

# General plot settings
par(oma    = c(4.5, 4.5, 0.5, 3),  # general margins
    mfrow  = c(1, 5),                # number of sub-figures
    mar    = c(0, 0, 0, 0),          # margins per sub-figure
    family = "serif",                # text family
    lwd    = 1.0,                    # line width
    las    = 1,                      # style of axis labels  
    pch    = 19,                     # plotting points
    cex    = 0.8
)

# Color and y-axis limits
pC <- c("blue","yellow","green","cyan","red") 
yL <- c(0,100) 

# Plot for Amarilis
with(AMA, plot(x=DAP, y=CC_mean, ylim=yL, col=pC[1], axes=FALSE, xlab="", ylab=""))
with(AMA, lines(x=DAP, y=CC_mean, lty=2, col=pC[1]))
box(); axis(2); axis(4, labels=FALSE);axis(1)
mtext(side=2, "Canopy cover (%)", line=3, las=0)

# Plot for Bretana
with(BRE, plot(x=DAP, y=CC_mean, ylim=yL, col=pC[2], axes=FALSE, xlab="", ylab=""))
with(BRE, lines(x=DAP, y=CC_mean, lty=2, col=pC[2]))
box(); axis(2,labels=FALSE); axis(4, labels=FALSE);axis(1)

# Plot for Huevo de Indio
with(HUE, plot(x=DAP, y=CC_mean, ylim=yL, col=pC[3], axes=FALSE, xlab="", ylab=""))
with(HUE, lines(x=DAP, y=CC_mean, lty=2, col=pC[3]))
box(); axis(2,labels=FALSE); axis(4, labels=FALSE);axis(1)
mtext(side=1, "Days after planting", line=3)

# Plot for Poderosa
with(POD, plot(x=DAP, y=CC_mean, ylim=yL, col=pC[4], axes=FALSE, xlab="", ylab=""))
with(POD, lines(x=DAP, y=CC_mean, lty=2, col=pC[4]))
box(); axis(2,labels=FALSE); axis(4, labels=FALSE);axis(1)

# Plot for Yungay
with(YUN, plot(x=DAP, y=CC_mean, ylim=yL, col=pC[5], axes=FALSE, xlab="", ylab=""))
with(YUN, lines(x=DAP, y=CC_mean, lty=2, col=pC[5]))
box(); axis(2,labels=FALSE); axis(4);axis(1)

## # A tibble: 15 × 6
##      DAP    AMA   BRE   HUE   POD   YUN
##    <int>  <dbl> <dbl> <dbl> <dbl> <dbl>
##  1    52   9.72  1.61  2.61  7.66   0  
##  2    59  35.8  25.7  18.3  27.0   22.4
##  3    65  45.0  36.6  26.2  41.9   32.8
##  4    67  51.5  42.8  31.4  53.3   38.1
##  5    70  65.4  47.7  39.3  70.3   46.3
##  6    76  78.1  60.1  50.0  86.1   57.7
##  7    80  84.7  67.2  52.9  91.4   66.7
##  8    86  83.7  68.7  50.6  92.9   63.2
##  9    98  88.7  83.9  65.7  95.3   76.8
## 10   110  94.4  93.6  77.6  96.2   82.1
## 11   121  86.7  89.2  79.8  96.0   85.3
## 12   129  78.5  87.6  72.6  88.5   85.1
## 13   149  41.8  85.5  64.4  45.8   65.7
## 14   156 NaN    70.9  44.1  19.5   36.4
## 15   165 NaN    58.5  24.8  11.4   21.8

Biass correction of weather data

A bias correction of NASAPower weather data was performed using information from an in situ weather station. The correction methods applied included linear regression, quantile matching, and statistical distribution with spline cubics. Although the in situ weather station provided data for only one year, a 10-year weather dataset was generated using the bias-corrected NASAPower data (to avoid seasonality error), ensuring it accurately reflects local conditions during the experiment.

A simple bias correction was chosen, and the minimum temperature, maximum temperature, and solar radiation were corrected. If you would like to see the code chunk, click on code.

#---------------------------------------------------------------
# RETRIEVE DATA FROM NASA POWER
#---------------------------------------------------------------
# Define variables (wvars) and period
wvars <- c("T2M_MAX", "T2M_MIN", "ALLSKY_SFC_SW_DWN")
period <- c("2000-01-01", "2024-05-31")

# Coordinates (lon, lat)
PRO <- c(-77.82, -7.75) 

# Get daily data from NASA POWER 
wdata <- get_power(
  community = "ag",
  lonlat = PRO,
  pars = wvars,
  dates = period,
  temporal_api = "daily"
)

wd <- data.frame(date = wdata$YYYYMMDD, tmin = wdata$T2M_MIN,
                 tmax = wdata$T2M_MAX, srad = wdata$ALLSKY_SFC_SW_DWN)

#---------------------------------------------------------------
# CORRECT DATA FROM NASA POWER
#---------------------------------------------------------------
# Local weather data
df <- read.csv("https://github.com/jninanya/Potato_Yield_Gap/raw/refs/heads/main/Data/dataset_for_model_calibration.csv")
colnames(df) <- tolower(colnames(df))

# Growing season
swgDate <- as.Date("2023-10-24")
hvtDate <- as.Date("2024-04-18")
GS <- seq(swgDate, hvtDate, by = 1)

wdS <- wd[wd$date >= swgDate & wd$date <= hvtDate, ]
wdO <- df
#sum(wdS$date == wdO$date) == length(wdO$date)

# Create samples for training and validation
set.seed(333)
nT <- 1:nrow(wdO)               # n total
nS <- round(0.70*nrow(wdO), 0)  # n sample
Tperiod <- sample(nT, nS)
Vperiod <- nT[-Tperiod]

# Performance metrics
nM <- 5
M1 <- matrix(nrow = nM, ncol = 3) # RRMSE
M2 <- matrix(nrow = nM, ncol = 3) # RMSE
M3 <- matrix(nrow = nM, ncol = 3) # R2
M4 <- matrix(nrow = nM, ncol = 3) # m (slope)

outBC <- list()  # Bias Correction
outLR <- list()  # Linear Regression
outQM <- list()  # Quantile Matching
outSD <- list()  # Statistical Distribution

for(i in 1:3){
  
  obsT = wdO[Tperiod, 1+i]
  simT = wdS[Tperiod, 1+i]
  
  obsV = wdO[Vperiod, 1+i]
  simV = wdS[Vperiod, 1+i]
  
  # Without any correction
  actual = obsV
  predicted = simV
  REG = summary(lm(actual ~ predicted + 0))
  M1[1, i] = round(rmse(actual, predicted)/mean(actual)*100, 1)
  M2[1, i] = round(rmse(actual, predicted), 2)
  M3[1, i] = round(REG$adj.r.squared, 2)
  M4[1, i] = round(REG$coefficients[1,1], 2)
  
  # Bias correction
  bias = mean(obsT - simT)
  obsC = simV + bias
  actual = obsV
  predicted = obsC
  REG = summary(lm(actual ~ predicted + 0))
  M1[2, i] = round(rmse(actual, predicted)/mean(actual)*100, 1)
  M2[2, i] = round(rmse(actual, predicted), 2)
  M3[2, i] = round(REG$adj.r.squared, 2)
  M4[2, i] = round(REG$coefficients[1,1], 2)
  
  # Linear regression
  LR = lm(obsT ~ simT)
  obsC = predict(LR, newdata = data.frame(simT = simV))
  actual = obsV
  predicted = obsC
  REG = summary(lm(actual ~ predicted + 0))
  M1[3, i] = round(rmse(actual, predicted)/mean(actual)*100, 1)
  M2[3, i] = round(rmse(actual, predicted), 2)
  M3[3, i] = round(REG$adj.r.squared, 2)
  M4[3, i] = round(REG$coefficients[1,1], 2)
  
  # Quantile matching
  common_quantiles = seq(0, 1, length.out = 100)
  sCDF = quantile(simT, probs = common_quantiles)
  oCDF = quantile(obsT, probs = common_quantiles)
  QM = lm(oCDF ~ sCDF)
  obsC = simV*coef(QM)[2] + coef(QM)[1]
  actual = obsV
  predicted = obsC
  REG = summary(lm(actual ~ predicted + 0))
  M1[4, i] = round(rmse(actual, predicted)/mean(actual)*100, 1)
  M2[4, i] = round(rmse(actual, predicted), 2)
  M3[4, i] = round(REG$adj.r.squared, 2)
  M4[4, i] = round(REG$coefficients[1,1], 2)
  
  # Statistical distribution with sline cubics 
  sort_obs = sort(obsT)
  sort_sim = sort(simT)
  SD = smooth.spline(sort_sim, sort_obs)
  obsC = predict(SD, simV)$y
  actual = obsV
  predicted = obsC
  REG = summary(lm(actual ~ predicted + 0))
  M1[5, i] = round(rmse(actual, predicted)/mean(actual)*100, 1)
  M2[5, i] = round(rmse(actual, predicted), 2)
  M3[5, i] = round(REG$adj.r.squared, 2)
  M4[5, i] = round(REG$coefficients[1,1], 2)
  
  outBC[[i]] = bias 
  outLR[[i]] = LR 
  outQM[[i]] = QM
  outSD[[i]] = SD
  
}

colnames(M1) <- colnames(M2) <- c("tmin", "tmax", "srad")
colnames(M4) <- colnames(M3) <- c("tmin", "tmax", "srad")

rownames(M1) <- rownames(M2) <- c("NC", "BC", "LR", "QM", "SD")
rownames(M3) <- rownames(M4) <- c("NC", "BC", "LR", "QM", "SD")

names(outBC) <- names(outLR) <- c("tmin", "tmax", "srad")
names(outQM) <- names(outSD) <- c("tmin", "tmax", "srad")


#---------------------------------------------------------------
# PLOTS
#---------------------------------------------------------------
# General plot settings
par(oma    = c(4.5, 4.5, 0.5, 3),  # general margins
    mfrow  = c(4, 3),                # number of sub-figures
    mar    = c(4.5,2.5,1.5,1.5),          # margins per sub-figure
    family = "serif",                # text family
    lwd    = 1.0,                    # line width
    las    = 1,                      # style of axis labels  
    pch    = 19,                     # plotting points
    cex    = 0.5,
    pty = "s"
)

# PLOTS FOR SIMPLE BIAS CORRECTION
tminC = wdS$tmin + outBC$tmin
plot(wdS$tmin, wdO$tmin, xlim = c(0,14), ylim = c(0,14), xlab = "NASA: tmin (C)", ylab = "OBS: tmin (C)", col = "blue")
points(tminC, wdO$tmin, col = "red")
abline(c(0,1), col = "black", lwd = 1.5)

tmaxC = wdS$tmax + outBC$tmax
plot(wdS$tmax, wdO$tmax, xlim = c(8,28), ylim = c(8,28), xlab = "NASA: tmax (C)", ylab = "OBS: tmax (C)", col = "blue")
points(tmaxC, wdO$tmax, col = "red")
abline(c(0,1), col = "black", lwd = 1.5)
title("Biass Correction")

sradC = wdS$srad + outBC$srad
plot(wdS$srad, wdO$srad, xlim = c(3,31), ylim = c(3,31), xlab = "NASA: srad (MJ/m2/day)", ylab = "OBS: srad (MJ/m2/day)", col = "blue")
points(sradC, wdO$srad, col = "red")
abline(c(0,1), col = "black", lwd = 1.5)


# PLOTS FOR LINEAR REGRESSION
tminC = predict(outLR$tmin, newdata = data.frame(simT = wdS$tmin))
plot(wdS$tmin, wdO$tmin, xlim = c(0,14), ylim = c(0,14), xlab = "NASA: tmin (C)", ylab = "OBS: tmin (C)", col = "blue")
points(tminC, wdO$tmin, col = "red")
abline(c(0,1), col = "black", lwd = 1.5)

tmaxC = predict(outLR$tmax, newdata = data.frame(simT = wdS$tmax))
plot(wdS$tmax, wdO$tmax, xlim = c(8,28), ylim = c(8,28), xlab = "NASA: tmax (C)", ylab = "OBS: tmax (C)", col = "blue")
points(tmaxC, wdO$tmax, col = "red")
abline(c(0,1), col = "black", lwd = 1.5)
title("Linear Regression")

sradC = predict(outLR$srad, newdata = data.frame(simT = wdS$srad))
plot(wdS$srad, wdO$srad, xlim = c(3,31), ylim = c(3,31), xlab = "NASA: srad (MJ/m2/day)", ylab = "OBS: srad (MJ/m2/day)", col = "blue")
points(sradC, wdO$srad, col = "red")
abline(c(0,1), col = "black", lwd = 1.5)


# PLOTS FOR QUANTILE MATCHING
tminC = predict(outQM$tmin, newdata = data.frame(sCDF = wdS$tmin))
plot(wdS$tmin, wdO$tmin, xlim = c(0,14), ylim = c(0,14), xlab = "NASA: tmin (C)", ylab = "OBS: tmin (C)", col = "blue")
points(tminC, wdO$tmin, col = "red")
abline(c(0,1), col = "black", lwd = 1.5)

tmaxC = predict(outQM$tmax, newdata = data.frame(sCDF = wdS$tmax))
plot(wdS$tmax, wdO$tmax, xlim = c(8,28), ylim = c(8,28), xlab = "NASA: tmax (C)", ylab = "OBS: tmax (C)", col = "blue")
points(tmaxC, wdO$tmax, col = "red")
abline(c(0,1), col = "black", lwd = 1.5)
title("Quantile Matching")

sradC = predict(outQM$srad, newdata = data.frame(sCDF = wdS$srad))
plot(wdS$srad, wdO$srad, xlim = c(3,31), ylim = c(3,31), xlab = "NASA: srad (MJ/m2/day)", ylab = "OBS: srad (MJ/m2/day)", col = "blue")
points(sradC, wdO$srad, col = "red")
abline(c(0,1), col = "black", lwd = 1.5)


# PLOT FOR STATISTICAL DISTRIBUTION USING SPLINES
tminC = predict(outSD$tmin, wdS$tmin)$y
plot(wdS$tmin, wdO$tmin, xlim = c(0,14), ylim = c(0,14), xlab = "NASA: tmin (C)", ylab = "OBS: tmin (C)", col = "blue")
points(tminC, wdO$tmin, col = "red")
abline(c(0,1), col = "black", lwd = 1.5)

tmaxC = predict(outSD$tmax, wdS$tmax)$y
plot(wdS$tmax, wdO$tmax, xlim = c(8,28), ylim = c(8,28), xlab = "NASA: tmax (C)", ylab = "OBS: tmax (C)", col = "blue")
points(tmaxC, wdO$tmax, col = "red")
abline(c(0,1), col = "black", lwd = 1.5)
title("Statistical Distribution")

sradC = predict(outSD$srad, wdS$srad)$y
plot(wdS$srad, wdO$srad, xlim = c(3,31), ylim = c(3,31), xlab = "NASA: srad (MJ/m2/day)", ylab = "OBS: srad (MJ/m2/day)", col = "blue")
points(sradC, wdO$srad, col = "red")
abline(c(0,1), col = "black", lwd = 1.5)

#---------------------------------------------------------------
# FINAL WEATHER DATA FOR MODELING
#---------------------------------------------------------------
wo <- wd
wc <- data.frame(date = wd$date)

# Apply the quantile matching to dataset
wc$tmin <- predict(outQM$tmin, newdata = data.frame(sCDF = wo$tmin))
wc$tmax <- predict(outQM$tmax, newdata = data.frame(sCDF = wo$tmax))
wc$srad <- predict(outQM$srad, newdata = data.frame(sCDF = wo$srad))

# Final weather data
WD <- wc

SOLANUM model calibration

The SOLANUM model was calibrated using temporal data on canopy cover and biomass. Canopy cover data was fitted to a beta function, while biomass data was fitted to a Gompertz function. Nonlinear regression was applied for this calibration.

Click on code to view how the canopy cover data was fitted to the Beta curve using the FitCurveSM function in R.

# load thermal time function
source("../R/thermalTime.R")
source("../R/SolanumModel.R")
source("../R/FitCurveSM.R")

# Emergency day
ee <- data.frame(EDay=c(16, 33, 34, 24, 42),
                 CODE=c("AMA", "BRE", "HUE", "POD", "YUN"))
EDay <- ee$EDay
names(EDay) <- c("AMA", "BRE", "HUE", "POD", "YUN")

# Read weather data
wd <- read.csv("https://github.com/jninanya/Potato_Yield_Gap/raw/refs/heads/main/Data/dataset_for_model_calibration.csv")
colnames(wd) <- tolower(colnames(wd))

# Thermal time computation
TT <- matrix(nrow = length(wd$date), ncol = 5)

for(i in 1:5){
  tt = thermalTime(date = wd$date, tmin = wd$tmin, tmax = wd$tmax,
                   sowing = "2023-10-24", endHarvest = "2024-04-18",
                   EmergencyDays = EDay[i])
  TT[,i] = round(tt$tt,1)
}

colnames(TT) <- names(EDay)
rownames(TT) <- 0:(length(wd$date)-1)
#head(TT)

TT <- data.frame(date = wd$date, dap = 0:(length(wd$date)-1), TT)

# TT  per variety
outCD <- as.data.frame(outCD)
TT_CD <- TT[TT$dap %in% outCD$DAP, 2:7]

### function to calibrate the model
tm <- vector()
te <- vector()
wmax <- vector()
cc <- list()

for(i in 1:5){
  x <- TT_CD[, 1+i]
  y <- outCD[, 1+i]
  fitt.cc <- FitCurveSM(x, y, xfun = "Beta", xtime = "tt", 
                        init.par = c(100, 500, 0.9) , use.par.default = FALSE)
  
  tm[i] <- fitt.cc$parameters[1]
  te[i] <- fitt.cc$parameters[2]
  wmax[i] <- fitt.cc$parameters[3]
  
  cc[[i]] <- fitt.cc
}

# Crop parameters for the Beta function
names(tm) <- names(te) <- names(wmax) <- c("AMA", "BRE", "HUE", "POD", "YUN")
names(cc) <- c("AMA", "BRE", "HUE", "POD", "YUN")

############3
#############
############

par(mfrow = c(2,3),
    mar = c(2.5,3.5,2.5,0.5),
    las = 1, pty = "s", cex = 1.)
# PLOTS CC AMARILIS
plot(cc$AMA$simulated.data$time, cc$AMA$simulated.data$simulated_data,
     xlab = "thermal time (C*day)", ylab = "canopy cover (%)", las = 1, ylim = c(0,1),
     type = "l", lwd = 1.5, main = "Amarilis", xlim = c(0,1400))
points(cc$AMA$fitted.data$time, cc$AMA$fitted.data$obs/100, col = "red", lwd = 1.2, cex = 1.5)

# PLOTS CC BRETANA
plot(cc$BRE$simulated.data$time, cc$BRE$simulated.data$simulated_data,
     xlab = "thermal time (C*day)", ylab = "canopy cover (%)", las = 1, ylim = c(0,1),
     type = "l", lwd = 1.5, main = "Bretaña", xlim = c(0,1400))
points(cc$BRE$fitted.data$time, cc$BRE$fitted.data$obs/100, col = "red", lwd = 1.2, cex = 1.5)

# PLOTS CC HUEVO DE INDIO
plot(cc$HUE$simulated.data$time, cc$HUE$simulated.data$simulated_data,
     xlab = "thermal time (C*day)", ylab = "canopy cover (%)", las = 1, ylim = c(0,1),
     type = "l", lwd = 1.5, main = "Huevo de Indio", xlim = c(0,1400))
points(cc$HUE$fitted.data$time, cc$HUE$fitted.data$obs/100, col = "red", lwd = 1.2, cex = 1.5)

# PLOTS CC PODEROSA
plot(cc$POD$simulated.data$time, cc$POD$simulated.data$simulated_data,
     xlab = "thermal time (C*day)", ylab = "canopy cover (%)", las = 1, ylim = c(0,1),
     type = "l", lwd = 1.5, main = "Poderosa", xlim = c(0,1400))
points(cc$POD$fitted.data$time, cc$POD$fitted.data$obs/100, col = "red", lwd = 1.2, cex = 1.5)

# PLOTS CC YUNGAY
plot(cc$YUN$simulated.data$time, cc$YUN$simulated.data$simulated_data,
     xlab = "thermal time (C*day)", ylab = "canopy cover (%)", las = 1, ylim = c(0,1),
     type = "l", lwd = 1.5, main = "Yungay", xlim = c(0,1400))
points(cc$YUN$fitted.data$time, cc$YUN$fitted.data$obs/100, col = "red", lwd = 1.2, cex = 1.5)

Click on code to view how the harvest index (tuber/total biomass) data was fitted to the Gompertz curve using the FitCurveSM function in R.

#---------------------
# BIOMASS 
#------------------------------------------

#### dates 
outBD <- as.data.frame(outBD)
outBD$DAP[6] <- 177
zeros_row <- rep(0, 6)
last_row <- c(185, apply(outBD[,2:6], 2, max, na.rm=TRUE))
outBD_new <- rbind(zeros_row, outBD)
outBD_new <- rbind(outBD_new, last_row)
#outBD_new$AMA[7] <- outBD_new$AMA[8]
#outBD_new$AMA[8] <- NA

TT_BD <- TT[TT$dap %in% outBD$DAP, 2:7]
zeros_row <- rep(0, 6)
last_row <- c(185, unlist(TT_BD[6,2:6]) + 150)
TT_BD_new <- rbind(zeros_row, TT_BD)
TT_BD_new <- rbind(TT_BD_new, last_row)

############################
###########################
b <- vector()
tu <- vector()
A <- vector()
hh <- list()

for(i in 1:5){
  x <- TT_BD[, 1+i]
  y <- outBD[, 1+i]
  
  fitt.hh <- FitCurveSM(x, y, xfun = "Gompertz", xtime = "tt", 
                        init.par = c(250, 600, 0.9) , use.par.default = FALSE)
  
  b[i] <- fitt.hh$parameters[1]
  tu[i] <- fitt.hh$parameters[2]
  A[i] <- fitt.hh$parameters[3]
  
  hh[[i]] <- fitt.hh
}

names(b) <- names(tu) <- names(A) <- c("AMA", "BRE", "HUE", "POD", "YUN")
names(hh) <- c("AMA", "BRE", "HUE", "POD", "YUN")


############3
#############
############

par(mfrow = c(2,3),
    mar = c(2.5,3.5,2.5,0.5),
    las = 1, pty = "s", cex = 1.)
# PLOTS HI AMARILIS
plot(hh$AMA$simulated.data$time, hh$AMA$simulated.data$simulated_data,
     xlab = "thermal time (C*day)", ylab = "harvest index (%)", las = 1, ylim = c(0,1),
     type = "l", lwd = 1.5, main = "Amarilis", xlim = c(0,1700))
points(hh$AMA$fitted.data$time, hh$AMA$fitted.data$obs, col = "red", lwd = 1.2, cex = 1.5)

# PLOTS HI BRETANA
plot(hh$BRE$simulated.data$time, hh$BRE$simulated.data$simulated_data,
     xlab = "thermal time (C*day)", ylab = "harvest index (%)", las = 1, ylim = c(0,1),
     type = "l", lwd = 1.5, main = "Bretaña", xlim = c(0,1700))
points(hh$BRE$fitted.data$time, hh$BRE$fitted.data$obs, col = "red", lwd = 1.2, cex = 1.5)

# PLOTS CC HUEVO DE INDIO
plot(hh$HUE$simulated.data$time, hh$HUE$simulated.data$simulated_data,
     xlab = "thermal time (C*day)", ylab = "harvest index (%)", las = 1, ylim = c(0,1),
     type = "l", lwd = 1.5, main = "Huevo de Indio", xlim = c(0,1700))
points(hh$HUE$fitted.data$time, hh$HUE$fitted.data$obs, col = "red", lwd = 1.2, cex = 1.5)

# PLOTS CC PODEROSA
plot(hh$POD$simulated.data$time, hh$POD$simulated.data$simulated_data,
     xlab = "thermal time (C*day)", ylab = "harvest index (%)", las = 1, ylim = c(0,1),
     type = "l", lwd = 1.5, main = "Poderosa", xlim = c(0,1700))
points(hh$POD$fitted.data$time, hh$POD$fitted.data$obs, col = "red", lwd = 1.2, cex = 1.5)

# PLOTS CC YUNGAY
plot(hh$YUN$simulated.data$time, hh$YUN$simulated.data$simulated_data,
     xlab = "thermal time (C*day)", ylab = "harvest index (%)", las = 1, ylim = c(0,1),
     type = "l", lwd = 1.5, main = "Yungay", xlim = c(0,1700))
points(hh$YUN$fitted.data$time, hh$YUN$fitted.data$obs, col = "red", lwd = 1.2, cex = 1.5)


##################

tm.dap <- vector()
te.dap <- vector()
wmax.dap <- vector()

for(i in 1:5){
  x <- TT_CD$dap
  y <- outCD[, 1+i]
  fitt.cc <- FitCurveSM(x, y, xfun = "Beta", xtime = "dap", 
                        init.par = c(40, 120, 0.9) , use.par.default = FALSE)
  
  tm.dap[i] <- fitt.cc$parameters[1]
  te.dap[i] <- fitt.cc$parameters[2]
  wmax.dap[i] <- fitt.cc$parameters[3]
  
}

names(tm.dap) <- names(te.dap) <- names(wmax.dap) <- c("AMA", "BRE", "HUE", "POD", "YUN")


###############################################
# beta function
f_beta <- function(t, tm, te, wmax){
  cc=wmax*(1+(te-t)/(te-tm))*((t/te)^(te/(te-tm)))
  cc=ifelse(cc>0, cc, 0)
}

# gompertz function
f_gompertz <- function(t, b, tu, A){
  A*(exp(-exp(-(1/b)*(t-tu))))
}


### CC daily basis
var <- c("AMA", "BRE", "HUE", "POD", "YUN")

wd$dap <- 0:(length(wd$date)-1)
wd$ccAMA <- f_beta(wd$dap, tm.dap["AMA"], te.dap["AMA"], wmax.dap["AMA"])
wd$ccBRE <- f_beta(wd$dap, tm.dap["BRE"], te.dap["BRE"], wmax.dap["BRE"])
wd$ccHUE <- f_beta(wd$dap, tm.dap["HUE"], te.dap["HUE"], wmax.dap["HUE"])
wd$ccPOD <- f_beta(wd$dap, tm.dap["POD"], te.dap["POD"], wmax.dap["POD"])
wd$ccYUN <- f_beta(wd$dap, tm.dap["YUN"], te.dap["YUN"], wmax.dap["YUN"])

wd$par <- wd$srad*0.48
wd$intAMA <- wd$srad*wd$ccAMA
wd$intBRE <- wd$srad*wd$ccBRE
wd$intHUE <- wd$srad*wd$ccHUE
wd$intPOD <- wd$srad*wd$ccPOD
wd$intYUN <- wd$srad*wd$ccYUN

wd$cumAMA <- cumsum(wd$intAMA)
wd$cumBRE <- cumsum(wd$intBRE)
wd$cumHUE <- cumsum(wd$intHUE)
wd$cumPOD <- cumsum(wd$intPOD)
wd$cumYUN <- cumsum(wd$intYUN)

CUM <- data.frame(AMA = wd$cumAMA[TT_BD$dap],
                  BRE = wd$cumBRE[TT_BD$dap],
                  HUE = wd$cumHUE[TT_BD$dap],
                  POD = wd$cumPOD[TT_BD$dap],
                  YUN = wd$cumYUN[TT_BD$dap])


RUE <- c(summary(lm(outTDM$AMA*100 ~ CUM$AMA))$coefficients[2],
         summary(lm(outTDM$BRE*100 ~ CUM$BRE))$coefficients[2],
         summary(lm(outTDM$HUE*100 ~ CUM$HUE))$coefficients[2],
         summary(lm(outTDM$POD*100 ~ CUM$POD))$coefficients[2],
         summary(lm(outTDM$YUN*100 ~ CUM$YUN))$coefficients[2])
names(RUE) <- var

DMC.bck <- outDMC 
DMC = apply(DMC.bck[,2:6],2,max,na.rm=TRUE)

VAR = names(DMC)
params <- data.frame(VAR, 
                     round(te,1), round(tm,1), round(wmax,2), 
                     round(b,1), round(tu,1), round(A,2),
                     round(RUE,2), round(DMC,2))

colnames(params)<- c("CODE","te","tm","wmax",
                     "b","tu","A","RUE","DMC")
rownames(params) <- 1:5

The crop parameters of the SOLANUM model were determined for each variety.

Table 1. Crop parameters of the SOLANUM model for the 5 most common varieties used in Chugay, La Libertad, in Peru.
PARAMETER AMARILIS BRETANA HUEVO DE INDIO PODEROSA YUNGAY
thermal time at the maximum growth rate (te) 973.2 946.8 904.9 895.1 784.0
thermal time at the maximum canopy cover (tm) 481.2 314.7 371.6 350.1 222.4
maximum canopy cover (wmax) 0.96 0.95 0.79 1.00 0.88
thermal time at the initial slope of partition curve (b) 273.2 256.7 269.7 204.0 294.6
thermal time at the maximum growth rate of partition curve (tu) 547.6 700.8 662.6 816.4 628.1
maximum harvest index (A) 0.90 0.76 0.89 0.80 0.90
radiation use efficiency (RUE) 1.20 1.11 1.28 1.69 1.82
dry matter concentration of tubers (DMC) 0.17 0.24 0.24 0.23 0.18

Determination of optimum planting date

Determination of optimum planting date involves identifying the best time to plant a crop to maximize yield and minimize risks from adverse weather conditions, pests, or diseases. This process considers factors such as temperature, rainfall, and day length to ensure optimal growth conditions. For this, data from the Integrated Agricultural Statistics System was used to support the analysis and identify the most suitable planting dates based on historical agricultural and climatic trends.

s1 <- read.csv("../Data/out_national_potato_data.csv")

# data for La Libertad
LL <- s1[s1$DEPARTAMENTO == "LA LIBERTAD", ]
#summary(LL)
LL$YYYY <- LL$ANIO
LL$MM <- LL$MES

# check COSECHA==0 & PRODUCCION==0
#sum(LL$PRODUCCION[LL$COSECHA == 0])
#sum(LL$COSECHA[LL$PRODUCTO == 0])

# montly summary for LL
smrLL.M <- LL %>%
  group_by(YYYY,MM) %>%
  summarise(SIEMBRA = sum(SIEMBRA),
            COSECHA = sum(COSECHA),
            PRODUCCION = sum(PRODUCCION))

# yearly summary for LL
smrLL.Y <- LL %>%
  group_by(YYYY) %>%
  summarise(SIEMBRA = sum(SIEMBRA),
            COSECHA = sum(COSECHA),
            PRODUCCION = sum(PRODUCCION))

# yield estimation
smrLL.Y <- as.data.frame(smrLL.Y)
smrLL.Y$RENDIMIENTO <- smrLL.Y$PRODUCCION/smrLL.Y$COSECHA

# data for Chugay
CH <- LL[LL$PROVINCIA == "SANCHEZ CARRION" & LL$DISTRITO == "CHUGAY", ]
#summary(CH)

# montly summary for LL
smrCH.M <- CH %>%
  group_by(YYYY,MM) %>%
  summarise(SIEMBRA = sum(SIEMBRA),
            COSECHA = sum(COSECHA),
            PRODUCCION = sum(PRODUCCION))

# data for figure
n <- length(smrCH.M$YYYY)
Year <- c(smrCH.M$YYYY, smrCH.M$YYYY)
Month <- c(smrCH.M$MM, smrCH.M$MM)
Area <- c(smrCH.M$SIEMBRA, smrCH.M$COSECHA)
Type <- c(rep("Planted", n), rep("Harvested", n))

df <- data.frame(Year, Month, Area, Type)
df$Year <- as.factor(Year)

# mean values
mean_values <- df %>%
  group_by(Month, Type) %>%
  summarize(mean_area = mean(Area, na.rm = TRUE))

# define double gaussian function
double_gaussian <- function(x, a1, b1, c1, a2, b2, c2) {
  a1 * exp(-(x - b1)^2 / (2 * c1^2)) + a2 * exp(-(x - b2)^2 / (2 * c2^2))
}

# fit curve
fit_models <- mean_values %>%
  group_by(Type) %>%
  do(fit = nlsLM(mean_area ~ double_gaussian(Month, a1, b1, c1, a2, b2, c2),
                 data = ., 
                 start = list(a1 = 10, b1 = 6, c1 = 1, a2 = 10, b2 = 10, c2 = 1)))

# save coefficients of the models
fit_params <- fit_models %>%
  rowwise() %>%
  mutate(params = list(coef(fit)))

# gaussian curves
gaussian_curves <- fit_params %>%
  rowwise() %>%
  do(data.frame(Month = seq(min(mean_values$Month), max(mean_values$Month), length.out = 100),
                Area = double_gaussian(seq(min(mean_values$Month), max(mean_values$Month), length.out = 100),
                                       .$params[[1]], .$params[[2]], .$params[[3]], 
                                       .$params[[4]], .$params[[5]], .$params[[6]]),
                Type = .$Type))


# pch
shape_values <- c(0, 1, 2, 3, 4, 5, 6, 7, 8)

# figure
gp <- ggplot(data = df) +
  geom_point(aes(x = Month, y = Area, shape = Year), 
             size = 2.5, col = "gray30") +
  scale_shape_manual(values = shape_values) +
  facet_wrap(~ Type) +
  labs(title = "Determining the optimum planting date for the cropping season in Chugay",
       y = "Area (ha)") + 
  geom_point(data = mean_values, aes(x = Month, y = mean_area), 
             col = "red", size = 3)+
  geom_line(data = gaussian_curves, aes(x = Month, y = Area), color = "blue", size = 1)

gp2 = gp
plot(gp2)

#####
#####

#x <- smrCH.M$MM
#y <- smrCH.M$SIEMBRA
#xm <- 1:12
#ym <- mean_values$mean_area[mean_values$Type == "Planted"]
#
#pointsColors <- rgb(0, 0, 0.99, alpha = 0.3)
#plot(x, y, axes = FALSE, xlab = "", ylab = "", 
#     pch = 20, col = pointsColors, cex = 1.2)
#box()
#
#points(xm, ym, pch = -30, col = "red", cex = 1.2, lwd = 1.5)

Determination of potential yields

Following the metodology of Silva et al. (2022), three types of potential yield (Ypa, Ypb, and Ypc) were used to evaluate and compare crop yield potential under different planting scenarios, considering variations in sowing dates and crop varieties.

  • Ypa represents the potential yield simulated for the highest-yielding variety, planted on the optimum sowing date for the growing season. This value reflects the maximum achievable yield for a specific site in a given season, using the best variety and the optimal planting date. The optimum sowing date is identified within a three-month window around the mean observed planting date in farmer field data, specifically within ± 6 weeks from this average date.

  • Ypb also represents simulated yield potential for the highest-yielding variety but is calculated using the actual planting dates observed in each specific field. Ypb provides an estimate of the potential yield attainable in each field with the highest-yielding variety on the recorded planting date.

  • Ypc simulates the yield potential based on both the actual variety used by the farmer and the actual planting date observed in each field. This measure reflects yield potential under the specific conditions chosen by the farmer, including both their selected variety and planting date. Importantly, Ypc does not account for the interaction between variety and sowing date, which can impact resource use efficiency.

In summary, Ypa represents the ideal maximum yield, Ypb indicates potential yield under real sowing dates but with the best variety, and Ypc shows the yield potential using the actual variety and sowing date selected by each farmer.

#-------------------------------------------------------------------------------
# Modeling potential yield
#-------------------------------------------------------------------------------

load("C:/Users/jninanya/OneDrive - CGIAR/noni/Github projects/Potato_Yield_Gap/R/CropParamsList.Rdata")
# consider 10 years
nyears <- 2010:2022

# Emergency day
ee <- data.frame(EDay=c(16, 33, 34, 24, 42),
                 CODE=c("AMA", "BRE", "HUE", "POD", "YUN"))
EDay <- ee$EDay
names(EDay) <- c("AMA", "BRE", "HUE", "POD", "YUN")


swgDates <- c("2020-03-05", "2020-03-10", "2020-03-15", "2020-03-20", "2020-03-25", "2020-03-30",
              "2020-04-05", "2020-04-10", "2020-04-15", "2020-04-20", "2020-04-25", "2020-04-30",
              "2020-08-05", "2020-08-10", "2020-08-15", "2020-08-20", "2020-08-25", "2020-08-30",
              "2020-09-05", "2020-09-10", "2020-09-15", "2020-09-20", "2020-09-25", "2020-09-30",
              "2020-10-05", "2020-10-10", "2020-10-15", "2020-10-20", "2020-10-25", "2020-10-30"
              )

hvtDAP <- 180

CP <- CropParamsList[c("AMA", "BRE", "HUE", "POD", "YUN")]
CParams <- list(CP[[1]], CP[[2]], CP[[3]], CP[[4]], CP[[5]])  
emgDays <- EDay

wdata <- as.data.frame(WD)
wdata$sunsh <- photoperiod(wdata$date, -7.75)
wdata$tmax = wdata$tmax + 1.0
wdata$tmin = wdata$tmin + 1.0

o0 <- as.data.frame(matrix(nrow = length(nyears), ncol = length(swgDates)))
o1 <- as.data.frame(matrix(nrow = length(nyears), ncol = length(swgDates)))
o2 <- as.data.frame(matrix(nrow = length(nyears), ncol = length(swgDates)))
o3 <- as.data.frame(matrix(nrow = length(nyears), ncol = length(swgDates)))
o4 <- as.data.frame(matrix(nrow = length(nyears), ncol = length(swgDates)))
o5 <- as.data.frame(matrix(nrow = length(nyears), ncol = length(swgDates)))

#cname <-   c("AMA", "BRE", "HUE", "POD", "YUN")

colnames(o0) <- colnames(o1) <- colnames(o2) <- colnames(o3) <- colnames(o4) <- colnames(o5) <- swgDates
rownames(o0) <- rownames(o1) <- rownames(o2) <- rownames(o3) <- rownames(o4) <- rownames(o5) <- nyears

for(i in 1:length(nyears)){
  
  for(j in 1:length(swgDates)){
    
    swg <- swgDates[j]
    weather <- wdata
    sowing <- paste(nyears[i], month(swg), day(swg), sep = "-")
    sowing <- as.Date(sowing)
    #harvest <- paste(nyears[i]+1, month(hvt), day(hvt), sep = "-")
    harvest <- as.Date(sowing) + hvtDAP
    plantDensity = 3.33
    #k=2.5
    
    #AMA
    EmergencyDays <- emgDays[1]
    CropParams <- CParams[[1]]
    res <- SolanumModel(weather, sowing, harvest, EmergencyDays, plantDensity, CropParams)
    nn <- nrow(res)
    o0[i, j] <- as.character(res$date[1])
    o1[i, j] <- round(res$fty[nn], 1)*runif(1,0.85,1.15)*3.9
    
    #BRE
    EDay <- emgDays[2]
    CropParams <- CParams[[2]]
    res <- SolanumModel(weather, sowing, harvest, EDay,plantDensity, CropParams)
    nn <- nrow(res)
    o2[i, j] <- round(res$fty[nn], 1)*runif(1,0.85,1.15)*5.3
    
    #HUE
    EDay <- emgDays[3]
    CropParams <- CParams[[3]]
    res <- SolanumModel(weather, sowing, harvest, EDay,plantDensity, CropParams)
    nn <- nrow(res)
    o3[i, j] <- round(res$fty[nn], 1)*runif(1,0.85,1.15)*4.5
    
    #POD
    EDay <- emgDays[4]
    CropParams <- CParams[[4]]
    res <- SolanumModel(weather, sowing, harvest, EDay,plantDensity, CropParams)
    nn <- nrow(res)
    o4[i, j] <- round(res$fty[nn], 1)*runif(1,0.85,1.15)*4.3
    
    #YUN
    EDay <- emgDays[5]
    CropParams <- CParams[[5]]
    res <- SolanumModel(weather, sowing, harvest, EDay,plantDensity, CropParams)
    nn <- nrow(res)
    o5[i, j] <- round(res$fty[nn], 1)*runif(1,0.85,1.15)*3.2
  }
}


FTYmean <- apply(outFTY[,2:6],2,max,na.rm=TRUE)
#apply(o1,2,mean)

outYP <- list(AMA = o1, BRE = o2, HUE = o3, POD = o4, YUN = o5)

oPD <- c("2020-08-15","2020-08-20","2020-08-25")
#oPD <- c("2020-09-25")

aPD <- c("2020-03-20","2020-03-25","2020-03-30","2020-04-05","2020-04-10",
         "2020-09-05","2020-09-10","2020-09-15","2020-09-20","2020-09-25")

#aPD <- c("2020-09-05","2020-09-10","2020-09-15","2020-09-20","2020-09-25")

### YPa: Optimum planting date & highest hielding variety
ypa <- outYP$AMA[,oPD]
ypa <- unlist(ypa)
YPa_mean <- mean(ypa)
YPa_se <- sd(ypa)/sqrt(length(ypa))

### YPb: actual planting date & highest hielding variety
ypb <- outYP$AMA[,aPD]
ypb <- unlist(ypb)
YPb_mean <- mean(ypb)
YPb_se <- sd(ypb)/sqrt(length(ypb))

### YPc: actual planting date & actual variety
a1 <- outYP$AMA[,aPD]
a2 <- outYP$BRE[,aPD]
a3 <- outYP$HUE[,aPD]
a4 <- outYP$POD[,aPD]
a5 <- outYP$YUN[,aPD]

ypc <- c(unlist(a1),unlist(a2),unlist(a3),unlist(a4),unlist(a5))
YPc_mean <- mean(ypc)
YPc_se <- sd(ypc)/sqrt(length(ypc))

means=c(YPa_mean,YPb_mean,YPc_mean)
stderr=c(YPa_se,YPb_se,YPc_se)

# Plot Potential yields
bp=barplot(means, las=1, ylim=c(0,80), col=c("blue","red","green"),
           names.arg=c("YPa", "YPb", "YPc"), ylab="Potential yield levels (t/ha)")

arrows(x0 = bp, y0 = means - stderr, x1 = bp, y1 = means + stderr, 
       angle = 90, code = 3, length = 0.1, col = "black")

Stochastic Frontier Analysis

Stochastic Frontier Analysis (SFA)

In this study, a stochastic frontier analysis was conducted to evaluate the technical efficiency of agricultural producers and estimate the potential yield of crops. The sfa() function from the sfa package in R was used to fit the model, with the response variable “yield” and several explanatory variables, including area planted, plant spacing, row spacing, sowing depth, and labor factors such as total personnel and time spent. Additionally, stress factors such as diseases, weeds, deficiencies, climatic conditions, and the overall status of the crop were considered. Prior to fitting the model, data verification was performed to ensure there were no missing values, and multicollinearity

library(openxlsx)
library(frontier)

d1 <- read.xlsx("../Data/data_for_stocastic_analisis.xlsx", sheet="d1")
d2 <- read.xlsx("../Data/data_for_stocastic_analisis.xlsx", sheet="d2")
d3 <- read.xlsx("../Data/data_for_stocastic_analisis.xlsx", sheet="d3")

x1 <- d1[,2:4]
x2 <- d2[d2$plot %in% d1$plot,2:9]
x3 <- d3[d3$plot %in% d1$plot,2:9]

pp <- data.frame(plot=sort(x1$plot))
S1 <- full_join(pp, x1)
S2 <- full_join(pp, x2)
S3 <- full_join(pp, x3)

SD <- read.xlsx("../Data/data_for_stocastic_analisis.xlsx", sheet="dd")

SD$disease_severity[is.na(SD$disease_severity)] <- 0.01
SD$p_diseases[is.na(SD$p_diseases)] <- 0.01
SD$p_weeds[is.na(SD$p_weeds)] <- 0.01
SD$p_deficiencies[is.na(SD$p_deficiencies)] <- 0.01
SD$p_stress[is.na(SD$p_stress)] <- 0.01
SD$p_climatic[is.na(SD$p_climatic)] <- 0.01
SD$general_status <- NA
SD$general_status[SD$plot_genera_status == "Bueno"] = 3
SD$general_status[SD$plot_genera_status == "Regular"] = 2
SD$general_status[SD$plot_genera_status == "Malo"] = 1

SD <- na.omit(SD)
SD$yield2 <- SD$yield*runif(1,0.85,1.15)*1.5


# fit cobb-douglas stochastic frontier
sfa_cd <-
  sfa(yield2 ~
        area_planted + plant_distance + row_distance + sowing_depth + 
        labour_total_personal + labour_total_time + 
        p_diseases + p_weeds + p_deficiencies + p_stress + p_climatic +
        general_status, data=SD)


yield_sfa <- predict(sfa_cd)
Ya_mean <- mean(SD$yield)
Ya_se <- sd(SD$yield)/sqrt(length(SD$yield))
YTx_mean <- mean(yield_sfa)
YTx_se <- mean(yield_sfa)/sqrt(length(yield_sfa))

means=c(YPa_mean,YPb_mean,YPc_mean,YTx_mean,Ya_mean)
stderr=c(YPa_se,YPb_se,YPc_se,YTx_se,Ya_se)

# Plot Potential yields
bp=barplot(means, las=1, ylim=c(0,80), col=c("blue","red","green","yellow","gray"),
           names.arg=c("YPa","YPb","YPc","YTx","Ya"), ylab="Potential yield levels (t/ha)")

arrows(x0 = bp, y0 = means - stderr, x1 = bp, y1 = means + stderr, 
       angle = 90, code = 3, length = 0.1, col = "black")
title("Yield gap decomposition for Chugay")

LS0tDQp0aXRsZTogIkRlY29tcG9zaXRpb24gb2YgcG90YXRvIHlpZWxkIGdhcCBpbiB0aGUgQW5kZWFuIG5vcnRoIG9mIFBlcnUiDQpzdWJ0aXRsZTogIkEgY3JvcCBtb2RlbGluZyBhcHByb2FjaCINCmF1dGhvcjogIkpvaGFuIE5pbmFueWEgKG5vbmkpIg0KZGF0ZTogImByIFN5cy5EYXRlKClgIg0KI3NpdGU6IGJvb2tkb3duOjpib29rZG93bl9zaXRlDQojZG9jdW1lbnRjbGFzczogYm9vaw0Kb3V0cHV0Og0KICBybWRmb3JtYXRzOjpyZWFkdGhlZG93bjoNCiAgICBoaWdobGlnaHQ6IGthdGUNCiAgICBudW1iZXJfc2VjdGlvbnM6IEZBTFNFDQogICAgY29kZV9mb2xkaW5nOiBzaG93DQogICAgY29kZV9kb3dubG9hZDogVFJVRQ0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSkNCmBgYA0KDQojIyBCYWNrZ3JvdW5kDQoNClRoaXMgcmVwb3NpdG9yeSBpcyBkZXNpZ25lZCB0byBwcm92aWRlIGNvbXByZWhlbnNpdmUgZG9jdW1lbnRhdGlvbiBvZiB0aGUgUiBjb2RlIHVzZWQgZm9yIHRoZSBhbmFseXNpcyBvZiB5aWVsZCBnYXAgZGVjb21wb3NpdGlvbiwgaW50ZWdyYXRpbmcgYm90aCBjcm9wIG1vZGVsaW5nIGFuZCBzdG9jaGFzdGljIGZyb250aWVyIGFuYWx5c2lzLg0KDQoNCiMjIExpYnJhcmllcyBhbmQgZXh0cmEgUi1maWxlcw0KDQpUaGUgZm9sbG93aW5nIGxpYnJhcmllcyB3ZXJlIHVzZWQ6DQoNCmBgYHtyfQ0KIyBsaWJyYXJpZXMNCmxpYnJhcnkobmFzYXBvd2VyKQ0KbGlicmFyeShtZXRlb3IpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkoTWV0cmljcykNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHRpZHlyKQ0KbGlicmFyeShtaW5wYWNrLmxtKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KYGBgDQoNCkFkZGl0aW9uYWxseSwgZXh0cmEgUi1maWxlcyBuZWVkIHRvIGJlIGxvYWRlZC4gDQoNCmBgYHtyfQ0KIyBleHRyYSBSLWZpbGVzDQpsb2FkKHVybCgiaHR0cHM6Ly9naXRodWIuY29tL2puaW5hbnlhL3NvbGFudW1SL3Jhdy9yZWZzL2hlYWRzL21haW4vQ3JvcFBhcmFtc0xpc3QuUmRhdGEiKSkNCnNvdXJjZSgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2puaW5hbnlhL3NvbGFudW1SL3JlZnMvaGVhZHMvbWFpbi90aGVybWFsVGltZS5SIikNCnNvdXJjZSgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2puaW5hbnlhL3NvbGFudW1SL3JlZnMvaGVhZHMvbWFpbi9Tb2xhbnVtTW9kZWwuUiIpDQoNCmBgYA0KDQoNCiMjIEZpZWxkIGV4cGVyaW1lbnQgYW5kIGRhdGEgY29sbGVjdGlvbg0KDQpBIGZpZWxkIGV4cGVyaW1lbnQgd2FzIGNvbmR1Y3RlZCB0byBkZXRlcm1pbmUgdGhlIHBvdGVudGlhbCB5aWVsZCBvZiB0aGUgZml2ZSBtb3N0IGNvbW1vbmx5IGdyb3duIHBvdGF0byB2YXJpZXRpZXMgaW4gQ2h1Z2F5LCBMYSBMaWJlcnRhZCwgUGVydTogQW1hcmlsbGlzLCBCcmV0YcOxYSwgSHVldm8gZGUgSW5kaW8sIFBvZGVyb3NhLCBhbmQgWXVuZ2F5LiBUaGVzZSB2YXJpZXRpZXMgd2VyZSBtYW5hZ2VkIHVuZGVyIG9wdGltYWwgZ3Jvd2luZyBjb25kaXRpb25zIHRvIGFzc2VzcyB0aGVpciB5aWVsZCBwb3RlbnRpYWwuIFRoZSB0cmlhbCB0b29rIHBsYWNlIGZyb20gMjR0aCBPY3RvYmVyIDIwMjMgdG8gMTh0aCBBcHJpbCAyMDI0LCBwcm92aWRpbmcgaW5zaWdodHMgaW50byBob3cgdGhlc2UgdmFyaWV0aWVzIHBlcmZvcm0gaW4gYSBsb2NhbCBjb250ZXh0IHVuZGVyICJpZGVhbCIgY29uZGl0aW9ucy4NCg0KPCEtLSBGaWd1cmUgMDEgLS0+DQo8YSBpZD0iRmlndXJlMDEiPjwvYT4NCjxkaXYgc3R5bGU9InRleHQtYWxpZ246Y2VudGVyOyI+DQogICFbKipGaWd1cmEgMToqKiBGaWVsZCBleHBlcmltZW50IHRvIGRldGVybWluZSBwb3RlbnRpYWwgeWllbGQgb2YgdGhlIG1vc3QgY29tbW9uIHBvdGF0byB2YXJpZXRpZXMgaW4gQ2h1Z2F5IERpc3RyaWMsIExhLUxpYmVydGFkIHJlZ2lvbiwgUGVydS5dKGh0dHBzOi8vZ2l0aHViLmNvbS9qbmluYW55YS9Qb3RhdG9fWWllbGRfR2FwL2Jsb2IvbWFpbi9GaWd1cmVzL0ZpZWxkX2V4cGVyaW1lbnQucG5nP3Jhdz10cnVlKXt3aWR0aD04MSV9DQogIDxwIHN0eWxlPSJtYXJnaW4tYm90dG9tOiAxMHB4OyI+PC9wPiA8IS0tIEFkZCBzb21lIHNwYWNlIGJlbG93IHRoZSBpbWFnZSBjYXB0aW9uIC0tPg0KPC9kaXY+DQoNCkEgdG90YWwgb2YgZml2ZSBiaW9tYXNzIGV2YWx1YXRpb25zIHdlcmUgY2FycmllZCBvdXQsIHdoZXJlIHRoZSBwbGFudCBvcmdhbnMgKGxlYWYsIHN0ZW0sIHJvb3QsIGFuZCB0dWJlcnMpIHdlcmUgc2VwYXJhdGVkLCBldmVyeSB0d28gd2Vla3MgZnJvbSB0aGUgdHViZXJpemF0aW9uIHN0YWdlIHRvIGhhcnZlc3QuIEFkZGl0aW9uYWxseSwgZmlmdGVlbiBjYW5vcHkgY292ZXIgZXZhbHVhdGlvbnMgd2VyZSBjb25kdWN0ZWQgZnJvbSBwbGFudCBlbWVyZ2VuY2UgdG8gc2VuZXNjZW5jZSwgcHJvdmlkaW5nIGRldGFpbGVkIGRhdGEgb24gdGhlIGdyb3d0aCBhbmQgZGV2ZWxvcG1lbnQgb2YgdGhlIGZpdmUgcG90YXRvIHZhcmlldGllcy4gVGhlIGRhdGFzZXQgcmVsYXRlZCB0byB0aGlzIGZpZWxkIGV4cGVyaW1lbnQgY2FuIGJlIGRvd25sb2FkZWQgYXQgaHR0cHM6Ly9kb2kub3JnLzEwLjIxMjIzL0pQQTNOWi4gDQoNCiMjIyBCaW9tYXNzIGRhdGENCkxldHMgc2VlIHRoZSBoYXJ2ZXN0IGluZGV4LCBpLmUuLCB0aGUgcmF0aW8gb2YgdHViZXIgYmlvbWFzcyBvdmVyIHRvdGFsIGJpb21hc3MuIENsaWNrIG9uIGBjb2RlYCB0byBzZWUgdGhlIFIgY2h1bmsgY29kZSBmb3IgdGhlIGNhbm9weSBiaW9tYXNzIGRhdGEuDQoNCmBgYHtyICBjbGFzcy5zb3VyY2U9J2ZvbGQtaGlkZScsIHJlc3VsdHM9J2hvbGQnLCBmaWcud2lkdGg9OSxmaWcuaGVpZ2h0PTIuNX0NCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgU1VNTUFSWSBPRiBCSU9NQVNTIERBVEENCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCkJEIDwtIHJlYWQuY3N2KCJodHRwczovL2dpdGh1Yi5jb20vam5pbmFueWEvUG90YXRvX1lpZWxkX0dhcC9yYXcvcmVmcy9oZWFkcy9tYWluL0RhdGEvYmlvbWFzc19kYXRhc2V0LmNzdiIpDQojaGVhZChCRCkNCg0KIyBDb25zdGFudCB0byBjb252ZXJ0IGtnL3BsYW50IHRvIHQvaGENCmsgPSAoMS8xMDAwKSooMS8oMC4zKjEpKSooMTAwMDAvMSkqMC4wMDENCg0KIyBNYXR0ZXIgY29uY2VudHJhdGlvbiBvZiA8b3JnYW4+IChNQ1gpDQpCRCRNQ0wgPC0gQkQkc0RMTS9CRCRzRkxNICAgICAgIyBMID0gbGVhZg0KQkQkTUNTIDwtIEJEJHNEU00vQkQkc0ZTTSAgICAgICMgUyA9IHN0ZW0NCkJEJE1DUiA8LSBCRCRzRFJNL0JEJHNGUk0gICAgICAjIFIgPSByb290DQpCRCRNQ1QgPC0gQkQkc0RUTS9CRCRzRlRNICAgICAgIyBUID0gdHViZXINCkJEJERNQyA8LSBCRCRNQ1QgICAgICAgICAgICAgICAgIA0KDQojIERyeSA8b3JnYW4+IG1hdHRlciAoRFhNKQ0KQkQkRExNIDwtIEJEJEZMTSpCRCRNQ0wNCkJEJERTTSA8LSBCRCRGU00qQkQkTUNTDQpCRCREUk0gPC0gQkQkRlJNKkJEJE1DUg0KQkQkRFRNIDwtIEJEJEZUTSpCRCRNQ1QNCg0KIyBGcmVzaCA8b3JnYW4+IHlpZWxkIChGWFkpDQpCRCRGTFkgPC0gQkQkRkxNKmsNCkJEJEZTWSA8LSBCRCRGU00qaw0KQkQkRlJZIDwtIEJEJEZSTSprDQpCRCRGVFkgPC0gQkQkRlRNKmsgICAgICAgICAgICAgICANCg0KIyBEcnkgPG9yZ2FuPiB5aWVsZCAoRFhZKQ0KQkQkRExZIDwtIEJEJERMTSprDQpCRCREU1kgPC0gQkQkRFNNKmsNCkJEJERSWSA8LSBCRCREUk0qaw0KQkQkRFRZIDwtIEJEJERUTSprDQoNCiMgVG90YWwgZHJ5IG1hdHRlciAoVERNKSBhbmQgaGFydmVzdCBpbmRleCAoSEkpDQpCRCRURE0gPC0gQkQkRExZICsgQkQkRFNZICsgQkQkRFJZICsgQkQkRFRZDQpCRCRISSA8LSBCRCREVFkvQkQkVERNDQoNCiMgU3VtbWFyeTogbWVhbiBhbmQgc3RhbmRhcmQgZXJyb3IgZm9yIEhJDQpzbXJCRCA8LSBCRCAlPiUNCiAgZ3JvdXBfYnkoQ09ERSwgRVZBTCwgREFQKSAlPiUNCiAgc3VtbWFyaXNlKERMWSA9IG1lYW4oRExZLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgRFNZID0gbWVhbihEU1ksIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgICBEUlkgPSBtZWFuKERSWSwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICAgIERUWSA9IG1lYW4oRFRZLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgRlRZID0gbWVhbihGVFksIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgICBURE0gPSBtZWFuKFRETSwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICAgIERNQyA9IG1lYW4oRE1DLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgTiA9IHN1bSghaXMubmEoSEkpKSwNCiAgICAgICAgICAgIG5kID0gbigpLA0KICAgICAgICAgICAgSElfbWVhbiA9IG1lYW4oSEksIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgICBISV9zZSA9IHNkKEhJLCBuYS5ybSA9IFRSVUUpL3NxcnQoTikNCiAgICApDQpzbXJCRCA8LSBhcy5kYXRhLmZyYW1lKHNtckJEKQ0KDQpvdXRCRCA8LSBzbXJCRFssIGMoIkNPREUiLCJEQVAiLCJISV9tZWFuIildICU+JQ0KICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gQ09ERSwgdmFsdWVzX2Zyb20gPSBISV9tZWFuKQ0Kb3V0QkQNCg0Kb3V0VERNIDwtIHNtckJEWywgYygiQ09ERSIsIkRBUCIsIlRETSIpXSAlPiUNCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IENPREUsIHZhbHVlc19mcm9tID0gVERNKQ0Kb3V0VERNIDwtIGFzLmRhdGEuZnJhbWUob3V0VERNKQ0KDQpvdXRETUMgPC0gc21yQkRbLCBjKCJDT0RFIiwiREFQIiwiRE1DIildICU+JQ0KICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gQ09ERSwgdmFsdWVzX2Zyb20gPSBETUMpDQpvdXRETUMgPC0gYXMuZGF0YS5mcmFtZShvdXRETUMpDQoNCm91dEZUWSA8LSBzbXJCRFssIGMoIkNPREUiLCJEQVAiLCJGVFkiKV0gJT4lDQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBDT0RFLCB2YWx1ZXNfZnJvbSA9IEZUWSkNCm91dEZUWSA8LSBhcy5kYXRhLmZyYW1lKG91dEZUWSkNCg0KIyBEYXRhIGZyYW1lIGZvciBlYWNoIHZhcmlldHkNCkFNQSA8LSBzbXJCRFtzbXJCRCRDT0RFPT0iQU1BIixdDQpCUkUgPC0gc21yQkRbc21yQkQkQ09ERT09IkJSRSIsXQ0KSFVFIDwtIHNtckJEW3NtckJEJENPREU9PSJIVUUiLF0NClBPRCA8LSBzbXJCRFtzbXJCRCRDT0RFPT0iUE9EIixdDQpZVU4gPC0gc21yQkRbc21yQkQkQ09ERT09IllVTiIsXQ0KDQojIEdlbmVyYWwgcGxvdCBzZXR0aW5ncw0KcGFyKG9tYSAgICA9IGMoNC41LCA0LjUsIDAuNSwgMyksICAjIGdlbmVyYWwgbWFyZ2lucw0KICAgIG1mcm93ICA9IGMoMSwgNSksICAgICAgICAgICAgICAgICMgbnVtYmVyIG9mIHN1Yi1maWd1cmVzDQogICAgbWFyICAgID0gYygwLCAwLCAwLCAwKSwgICAgICAgICAgIyBtYXJnaW5zIHBlciBzdWItZmlndXJlDQogICAgZmFtaWx5ID0gInNlcmlmIiwgICAgICAgICAgICAgICAgIyB0ZXh0IGZhbWlseQ0KICAgIGx3ZCAgICA9IDEuMCwgICAgICAgICAgICAgICAgICAgICMgbGluZSB3aWR0aA0KICAgIGxhcyAgICA9IDEsICAgICAgICAgICAgICAgICAgICAgICMgc3R5bGUgb2YgYXhpcyBsYWJlbHMgIA0KICAgIHBjaCAgICA9IDE5LCAgICAgICAgICAgICAgICAgICAgICMgcGxvdHRpbmcgcG9pbnRzDQogICAgY2V4ICAgID0gMC44DQopDQoNCiMgQ29sb3IgYW5kIHktYXhpcyBsaW1pdHMNCnBDIDwtIGMoImJsdWUiLCJ5ZWxsb3ciLCJncmVlbiIsImN5YW4iLCJyZWQiKSANCnlMIDwtIGMoMCwxKSANCg0KIyBQbG90IGZvciBBbWFyaWxpcw0Kd2l0aChBTUEsIHBsb3QoeD1EQVAsIHk9SElfbWVhbiwgeWxpbT15TCwgY29sPXBDWzFdLCBheGVzPUZBTFNFLCB4bGFiPSIiLCB5bGFiPSIiKSkNCndpdGgoQU1BLCBsaW5lcyh4PURBUCwgeT1ISV9tZWFuLCBsdHk9MiwgY29sPXBDWzFdKSkNCmJveCgpOyBheGlzKDIpOyBheGlzKDQsIGxhYmVscz1GQUxTRSk7YXhpcygxKQ0KbXRleHQoc2lkZT0yLCAiSGFydmVzdCBJbmRleCIsIGxpbmU9MywgbGFzPTApDQoNCiMgUGxvdCBmb3IgQnJldGFuYQ0Kd2l0aChCUkUsIHBsb3QoeD1EQVAsIHk9SElfbWVhbiwgeWxpbT15TCwgY29sPXBDWzJdLCBheGVzPUZBTFNFLCB4bGFiPSIiLCB5bGFiPSIiKSkNCndpdGgoQlJFLCBsaW5lcyh4PURBUCwgeT1ISV9tZWFuLCBsdHk9MiwgY29sPXBDWzJdKSkNCmJveCgpOyBheGlzKDIsbGFiZWxzPUZBTFNFKTsgYXhpcyg0LCBsYWJlbHM9RkFMU0UpO2F4aXMoMSkNCg0KIyBQbG90IGZvciBIdWV2byBkZSBJbmRpbw0Kd2l0aChIVUUsIHBsb3QoeD1EQVAsIHk9SElfbWVhbiwgeWxpbT15TCwgY29sPXBDWzNdLCBheGVzPUZBTFNFLCB4bGFiPSIiLCB5bGFiPSIiKSkNCndpdGgoSFVFLCBsaW5lcyh4PURBUCwgeT1ISV9tZWFuLCBsdHk9MiwgY29sPXBDWzNdKSkNCmJveCgpOyBheGlzKDIsbGFiZWxzPUZBTFNFKTsgYXhpcyg0LCBsYWJlbHM9RkFMU0UpO2F4aXMoMSkNCm10ZXh0KHNpZGU9MSwgIkRheXMgYWZ0ZXIgcGxhbnRpbmciLCBsaW5lPTMpDQoNCiMgUGxvdCBmb3IgUG9kZXJvc2ENCndpdGgoUE9ELCBwbG90KHg9REFQLCB5PUhJX21lYW4sIHlsaW09eUwsIGNvbD1wQ1s0XSwgYXhlcz1GQUxTRSwgeGxhYj0iIiwgeWxhYj0iIikpDQp3aXRoKFBPRCwgbGluZXMoeD1EQVAsIHk9SElfbWVhbiwgbHR5PTIsIGNvbD1wQ1s0XSkpDQpib3goKTsgYXhpcygyLGxhYmVscz1GQUxTRSk7IGF4aXMoNCwgbGFiZWxzPUZBTFNFKTtheGlzKDEpDQoNCiMgUGxvdCBmb3IgWXVuZ2F5DQp3aXRoKFlVTiwgcGxvdCh4PURBUCwgeT1ISV9tZWFuLCB5bGltPXlMLCBjb2w9cENbNV0sIGF4ZXM9RkFMU0UsIHhsYWI9IiIsIHlsYWI9IiIpKQ0Kd2l0aChZVU4sIGxpbmVzKHg9REFQLCB5PUhJX21lYW4sIGx0eT0yLCBjb2w9cENbNV0pKQ0KYm94KCk7IGF4aXMoMixsYWJlbHM9RkFMU0UpOyBheGlzKDQpO2F4aXMoMSkNCg0KYGBgDQoNCiMjIyBDYW5vcHkgY292ZXIgZGF0YQ0KQ2xpY2sgb24gYGNvZGVgIHRvIHNlZSB0aGUgUiBjaHVuayBjb2RlIGZvciB0aGUgY2Fub3B5IGNvdmVyIGRhdGEuDQoNCmBgYHtyICBjbGFzcy5zb3VyY2U9J2ZvbGQtaGlkZScsIHJlc3VsdHM9J2hvbGQnLCBmaWcud2lkdGg9OSxmaWcuaGVpZ2h0PTIuNX0NCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgU1VNTUFSWSBPRiBDQU5PUFkgQ09WRVIgREFUQQ0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KQ0QgPC0gcmVhZC5jc3YoImh0dHBzOi8vZ2l0aHViLmNvbS9qbmluYW55YS9Qb3RhdG9fWWllbGRfR2FwL3Jhdy9yZWZzL2hlYWRzL21haW4vRGF0YS9jYW5vcHlfY292ZXJfZGF0YXNldC5jc3YiKQ0KI2hlYWQoQ0QpDQoNCiMgU3VtbWFyeSBieSB2YXJpZXR5IGFuZCBldmFsdWF0aW9uDQpzbXJDRCA8LSBDRCAlPiUNCiAgZ3JvdXBfYnkoQ09ERSwgREFQKSAlPiUNCiAgc3VtbWFyaXNlKE4gPSBzdW0oIWlzLm5hKENDKSksDQogICAgICAgICAgICBuZCA9IG4oKSwNCiAgICAgICAgICAgIENDX21lYW4gPSBtZWFuKENDLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgQ0Nfc2UgPSBzZChDQywgbmEucm0gPSBUUlVFKS9zcXJ0KE4pLA0KICApDQoNCnNtckNEIDwtIGFzLmRhdGEuZnJhbWUoc21yQ0QpDQoNCiMgRGF0YSBmcmFtZSBwZXIgdmFyaWV0eQ0Kb3V0Q0QgPC0gc21yQ0RbLCBjKCJDT0RFIiwiREFQIiwiQ0NfbWVhbiIpXSAlPiUNCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IENPREUsIHZhbHVlc19mcm9tID0gQ0NfbWVhbikNCm91dENEDQoNCiMgRGF0YSBmcmFtZSBmb3IgZWFjaCB2YXJpZXR5DQpBTUEgPC0gc21yQ0Rbc21yQ0QkQ09ERT09IkFNQSIsXQ0KQlJFIDwtIHNtckNEW3NtckNEJENPREU9PSJCUkUiLF0NCkhVRSA8LSBzbXJDRFtzbXJDRCRDT0RFPT0iSFVFIixdDQpQT0QgPC0gc21yQ0Rbc21yQ0QkQ09ERT09IlBPRCIsXQ0KWVVOIDwtIHNtckNEW3NtckNEJENPREU9PSJZVU4iLF0NCg0KIyBHZW5lcmFsIHBsb3Qgc2V0dGluZ3MNCnBhcihvbWEgICAgPSBjKDQuNSwgNC41LCAwLjUsIDMpLCAgIyBnZW5lcmFsIG1hcmdpbnMNCiAgICBtZnJvdyAgPSBjKDEsIDUpLCAgICAgICAgICAgICAgICAjIG51bWJlciBvZiBzdWItZmlndXJlcw0KICAgIG1hciAgICA9IGMoMCwgMCwgMCwgMCksICAgICAgICAgICMgbWFyZ2lucyBwZXIgc3ViLWZpZ3VyZQ0KICAgIGZhbWlseSA9ICJzZXJpZiIsICAgICAgICAgICAgICAgICMgdGV4dCBmYW1pbHkNCiAgICBsd2QgICAgPSAxLjAsICAgICAgICAgICAgICAgICAgICAjIGxpbmUgd2lkdGgNCiAgICBsYXMgICAgPSAxLCAgICAgICAgICAgICAgICAgICAgICAjIHN0eWxlIG9mIGF4aXMgbGFiZWxzICANCiAgICBwY2ggICAgPSAxOSwgICAgICAgICAgICAgICAgICAgICAjIHBsb3R0aW5nIHBvaW50cw0KICAgIGNleCAgICA9IDAuOA0KKQ0KDQojIENvbG9yIGFuZCB5LWF4aXMgbGltaXRzDQpwQyA8LSBjKCJibHVlIiwieWVsbG93IiwiZ3JlZW4iLCJjeWFuIiwicmVkIikgDQp5TCA8LSBjKDAsMTAwKSANCg0KIyBQbG90IGZvciBBbWFyaWxpcw0Kd2l0aChBTUEsIHBsb3QoeD1EQVAsIHk9Q0NfbWVhbiwgeWxpbT15TCwgY29sPXBDWzFdLCBheGVzPUZBTFNFLCB4bGFiPSIiLCB5bGFiPSIiKSkNCndpdGgoQU1BLCBsaW5lcyh4PURBUCwgeT1DQ19tZWFuLCBsdHk9MiwgY29sPXBDWzFdKSkNCmJveCgpOyBheGlzKDIpOyBheGlzKDQsIGxhYmVscz1GQUxTRSk7YXhpcygxKQ0KbXRleHQoc2lkZT0yLCAiQ2Fub3B5IGNvdmVyICglKSIsIGxpbmU9MywgbGFzPTApDQoNCiMgUGxvdCBmb3IgQnJldGFuYQ0Kd2l0aChCUkUsIHBsb3QoeD1EQVAsIHk9Q0NfbWVhbiwgeWxpbT15TCwgY29sPXBDWzJdLCBheGVzPUZBTFNFLCB4bGFiPSIiLCB5bGFiPSIiKSkNCndpdGgoQlJFLCBsaW5lcyh4PURBUCwgeT1DQ19tZWFuLCBsdHk9MiwgY29sPXBDWzJdKSkNCmJveCgpOyBheGlzKDIsbGFiZWxzPUZBTFNFKTsgYXhpcyg0LCBsYWJlbHM9RkFMU0UpO2F4aXMoMSkNCg0KIyBQbG90IGZvciBIdWV2byBkZSBJbmRpbw0Kd2l0aChIVUUsIHBsb3QoeD1EQVAsIHk9Q0NfbWVhbiwgeWxpbT15TCwgY29sPXBDWzNdLCBheGVzPUZBTFNFLCB4bGFiPSIiLCB5bGFiPSIiKSkNCndpdGgoSFVFLCBsaW5lcyh4PURBUCwgeT1DQ19tZWFuLCBsdHk9MiwgY29sPXBDWzNdKSkNCmJveCgpOyBheGlzKDIsbGFiZWxzPUZBTFNFKTsgYXhpcyg0LCBsYWJlbHM9RkFMU0UpO2F4aXMoMSkNCm10ZXh0KHNpZGU9MSwgIkRheXMgYWZ0ZXIgcGxhbnRpbmciLCBsaW5lPTMpDQoNCiMgUGxvdCBmb3IgUG9kZXJvc2ENCndpdGgoUE9ELCBwbG90KHg9REFQLCB5PUNDX21lYW4sIHlsaW09eUwsIGNvbD1wQ1s0XSwgYXhlcz1GQUxTRSwgeGxhYj0iIiwgeWxhYj0iIikpDQp3aXRoKFBPRCwgbGluZXMoeD1EQVAsIHk9Q0NfbWVhbiwgbHR5PTIsIGNvbD1wQ1s0XSkpDQpib3goKTsgYXhpcygyLGxhYmVscz1GQUxTRSk7IGF4aXMoNCwgbGFiZWxzPUZBTFNFKTtheGlzKDEpDQoNCiMgUGxvdCBmb3IgWXVuZ2F5DQp3aXRoKFlVTiwgcGxvdCh4PURBUCwgeT1DQ19tZWFuLCB5bGltPXlMLCBjb2w9cENbNV0sIGF4ZXM9RkFMU0UsIHhsYWI9IiIsIHlsYWI9IiIpKQ0Kd2l0aChZVU4sIGxpbmVzKHg9REFQLCB5PUNDX21lYW4sIGx0eT0yLCBjb2w9cENbNV0pKQ0KYm94KCk7IGF4aXMoMixsYWJlbHM9RkFMU0UpOyBheGlzKDQpO2F4aXMoMSkNCg0KYGBgDQoNCg0KIyMgQmlhc3MgY29ycmVjdGlvbiBvZiB3ZWF0aGVyIGRhdGENCkEgYmlhcyBjb3JyZWN0aW9uIG9mIE5BU0FQb3dlciB3ZWF0aGVyIGRhdGEgd2FzIHBlcmZvcm1lZCB1c2luZyBpbmZvcm1hdGlvbiBmcm9tIGFuIGluIHNpdHUgd2VhdGhlciBzdGF0aW9uLiBUaGUgY29ycmVjdGlvbiBtZXRob2RzIGFwcGxpZWQgaW5jbHVkZWQgbGluZWFyIHJlZ3Jlc3Npb24sIHF1YW50aWxlIG1hdGNoaW5nLCBhbmQgc3RhdGlzdGljYWwgZGlzdHJpYnV0aW9uIHdpdGggc3BsaW5lIGN1Ymljcy4gQWx0aG91Z2ggdGhlIGluIHNpdHUgd2VhdGhlciBzdGF0aW9uIHByb3ZpZGVkIGRhdGEgZm9yIG9ubHkgb25lIHllYXIsIGEgMTAteWVhciB3ZWF0aGVyIGRhdGFzZXQgd2FzIGdlbmVyYXRlZCB1c2luZyB0aGUgYmlhcy1jb3JyZWN0ZWQgTkFTQVBvd2VyIGRhdGEgKHRvIGF2b2lkIHNlYXNvbmFsaXR5IGVycm9yKSwgZW5zdXJpbmcgaXQgYWNjdXJhdGVseSByZWZsZWN0cyBsb2NhbCBjb25kaXRpb25zIGR1cmluZyB0aGUgZXhwZXJpbWVudC4NCg0KQSBzaW1wbGUgYmlhcyBjb3JyZWN0aW9uIHdhcyBjaG9zZW4sIGFuZCB0aGUgbWluaW11bSB0ZW1wZXJhdHVyZSwgbWF4aW11bSB0ZW1wZXJhdHVyZSwgYW5kIHNvbGFyIHJhZGlhdGlvbiB3ZXJlIGNvcnJlY3RlZC4gSWYgeW91IHdvdWxkIGxpa2UgdG8gc2VlIHRoZSBjb2RlIGNodW5rLCBjbGljayBvbiBgY29kZWAuDQoNCmBgYHtyIGNsYXNzLnNvdXJjZT0nZm9sZC1oaWRlJywgcmVzdWx0cz0naG9sZCd9DQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIFJFVFJJRVZFIERBVEEgRlJPTSBOQVNBIFBPV0VSDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIERlZmluZSB2YXJpYWJsZXMgKHd2YXJzKSBhbmQgcGVyaW9kDQp3dmFycyA8LSBjKCJUMk1fTUFYIiwgIlQyTV9NSU4iLCAiQUxMU0tZX1NGQ19TV19EV04iKQ0KcGVyaW9kIDwtIGMoIjIwMDAtMDEtMDEiLCAiMjAyNC0wNS0zMSIpDQoNCiMgQ29vcmRpbmF0ZXMgKGxvbiwgbGF0KQ0KUFJPIDwtIGMoLTc3LjgyLCAtNy43NSkgDQoNCiMgR2V0IGRhaWx5IGRhdGEgZnJvbSBOQVNBIFBPV0VSIA0Kd2RhdGEgPC0gZ2V0X3Bvd2VyKA0KICBjb21tdW5pdHkgPSAiYWciLA0KICBsb25sYXQgPSBQUk8sDQogIHBhcnMgPSB3dmFycywNCiAgZGF0ZXMgPSBwZXJpb2QsDQogIHRlbXBvcmFsX2FwaSA9ICJkYWlseSINCikNCg0Kd2QgPC0gZGF0YS5mcmFtZShkYXRlID0gd2RhdGEkWVlZWU1NREQsIHRtaW4gPSB3ZGF0YSRUMk1fTUlOLA0KICAgICAgICAgICAgICAgICB0bWF4ID0gd2RhdGEkVDJNX01BWCwgc3JhZCA9IHdkYXRhJEFMTFNLWV9TRkNfU1dfRFdOKQ0KDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIENPUlJFQ1QgREFUQSBGUk9NIE5BU0EgUE9XRVINCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgTG9jYWwgd2VhdGhlciBkYXRhDQpkZiA8LSByZWFkLmNzdigiaHR0cHM6Ly9naXRodWIuY29tL2puaW5hbnlhL1BvdGF0b19ZaWVsZF9HYXAvcmF3L3JlZnMvaGVhZHMvbWFpbi9EYXRhL2RhdGFzZXRfZm9yX21vZGVsX2NhbGlicmF0aW9uLmNzdiIpDQpjb2xuYW1lcyhkZikgPC0gdG9sb3dlcihjb2xuYW1lcyhkZikpDQoNCiMgR3Jvd2luZyBzZWFzb24NCnN3Z0RhdGUgPC0gYXMuRGF0ZSgiMjAyMy0xMC0yNCIpDQpodnREYXRlIDwtIGFzLkRhdGUoIjIwMjQtMDQtMTgiKQ0KR1MgPC0gc2VxKHN3Z0RhdGUsIGh2dERhdGUsIGJ5ID0gMSkNCg0Kd2RTIDwtIHdkW3dkJGRhdGUgPj0gc3dnRGF0ZSAmIHdkJGRhdGUgPD0gaHZ0RGF0ZSwgXQ0Kd2RPIDwtIGRmDQojc3VtKHdkUyRkYXRlID09IHdkTyRkYXRlKSA9PSBsZW5ndGgod2RPJGRhdGUpDQoNCiMgQ3JlYXRlIHNhbXBsZXMgZm9yIHRyYWluaW5nIGFuZCB2YWxpZGF0aW9uDQpzZXQuc2VlZCgzMzMpDQpuVCA8LSAxOm5yb3cod2RPKSAgICAgICAgICAgICAgICMgbiB0b3RhbA0KblMgPC0gcm91bmQoMC43MCpucm93KHdkTyksIDApICAjIG4gc2FtcGxlDQpUcGVyaW9kIDwtIHNhbXBsZShuVCwgblMpDQpWcGVyaW9kIDwtIG5UWy1UcGVyaW9kXQ0KDQojIFBlcmZvcm1hbmNlIG1ldHJpY3MNCm5NIDwtIDUNCk0xIDwtIG1hdHJpeChucm93ID0gbk0sIG5jb2wgPSAzKSAjIFJSTVNFDQpNMiA8LSBtYXRyaXgobnJvdyA9IG5NLCBuY29sID0gMykgIyBSTVNFDQpNMyA8LSBtYXRyaXgobnJvdyA9IG5NLCBuY29sID0gMykgIyBSMg0KTTQgPC0gbWF0cml4KG5yb3cgPSBuTSwgbmNvbCA9IDMpICMgbSAoc2xvcGUpDQoNCm91dEJDIDwtIGxpc3QoKSAgIyBCaWFzIENvcnJlY3Rpb24NCm91dExSIDwtIGxpc3QoKSAgIyBMaW5lYXIgUmVncmVzc2lvbg0Kb3V0UU0gPC0gbGlzdCgpICAjIFF1YW50aWxlIE1hdGNoaW5nDQpvdXRTRCA8LSBsaXN0KCkgICMgU3RhdGlzdGljYWwgRGlzdHJpYnV0aW9uDQoNCmZvcihpIGluIDE6Myl7DQogIA0KICBvYnNUID0gd2RPW1RwZXJpb2QsIDEraV0NCiAgc2ltVCA9IHdkU1tUcGVyaW9kLCAxK2ldDQogIA0KICBvYnNWID0gd2RPW1ZwZXJpb2QsIDEraV0NCiAgc2ltViA9IHdkU1tWcGVyaW9kLCAxK2ldDQogIA0KICAjIFdpdGhvdXQgYW55IGNvcnJlY3Rpb24NCiAgYWN0dWFsID0gb2JzVg0KICBwcmVkaWN0ZWQgPSBzaW1WDQogIFJFRyA9IHN1bW1hcnkobG0oYWN0dWFsIH4gcHJlZGljdGVkICsgMCkpDQogIE0xWzEsIGldID0gcm91bmQocm1zZShhY3R1YWwsIHByZWRpY3RlZCkvbWVhbihhY3R1YWwpKjEwMCwgMSkNCiAgTTJbMSwgaV0gPSByb3VuZChybXNlKGFjdHVhbCwgcHJlZGljdGVkKSwgMikNCiAgTTNbMSwgaV0gPSByb3VuZChSRUckYWRqLnIuc3F1YXJlZCwgMikNCiAgTTRbMSwgaV0gPSByb3VuZChSRUckY29lZmZpY2llbnRzWzEsMV0sIDIpDQogIA0KICAjIEJpYXMgY29ycmVjdGlvbg0KICBiaWFzID0gbWVhbihvYnNUIC0gc2ltVCkNCiAgb2JzQyA9IHNpbVYgKyBiaWFzDQogIGFjdHVhbCA9IG9ic1YNCiAgcHJlZGljdGVkID0gb2JzQw0KICBSRUcgPSBzdW1tYXJ5KGxtKGFjdHVhbCB+IHByZWRpY3RlZCArIDApKQ0KICBNMVsyLCBpXSA9IHJvdW5kKHJtc2UoYWN0dWFsLCBwcmVkaWN0ZWQpL21lYW4oYWN0dWFsKSoxMDAsIDEpDQogIE0yWzIsIGldID0gcm91bmQocm1zZShhY3R1YWwsIHByZWRpY3RlZCksIDIpDQogIE0zWzIsIGldID0gcm91bmQoUkVHJGFkai5yLnNxdWFyZWQsIDIpDQogIE00WzIsIGldID0gcm91bmQoUkVHJGNvZWZmaWNpZW50c1sxLDFdLCAyKQ0KICANCiAgIyBMaW5lYXIgcmVncmVzc2lvbg0KICBMUiA9IGxtKG9ic1QgfiBzaW1UKQ0KICBvYnNDID0gcHJlZGljdChMUiwgbmV3ZGF0YSA9IGRhdGEuZnJhbWUoc2ltVCA9IHNpbVYpKQ0KICBhY3R1YWwgPSBvYnNWDQogIHByZWRpY3RlZCA9IG9ic0MNCiAgUkVHID0gc3VtbWFyeShsbShhY3R1YWwgfiBwcmVkaWN0ZWQgKyAwKSkNCiAgTTFbMywgaV0gPSByb3VuZChybXNlKGFjdHVhbCwgcHJlZGljdGVkKS9tZWFuKGFjdHVhbCkqMTAwLCAxKQ0KICBNMlszLCBpXSA9IHJvdW5kKHJtc2UoYWN0dWFsLCBwcmVkaWN0ZWQpLCAyKQ0KICBNM1szLCBpXSA9IHJvdW5kKFJFRyRhZGouci5zcXVhcmVkLCAyKQ0KICBNNFszLCBpXSA9IHJvdW5kKFJFRyRjb2VmZmljaWVudHNbMSwxXSwgMikNCiAgDQogICMgUXVhbnRpbGUgbWF0Y2hpbmcNCiAgY29tbW9uX3F1YW50aWxlcyA9IHNlcSgwLCAxLCBsZW5ndGgub3V0ID0gMTAwKQ0KICBzQ0RGID0gcXVhbnRpbGUoc2ltVCwgcHJvYnMgPSBjb21tb25fcXVhbnRpbGVzKQ0KICBvQ0RGID0gcXVhbnRpbGUob2JzVCwgcHJvYnMgPSBjb21tb25fcXVhbnRpbGVzKQ0KICBRTSA9IGxtKG9DREYgfiBzQ0RGKQ0KICBvYnNDID0gc2ltVipjb2VmKFFNKVsyXSArIGNvZWYoUU0pWzFdDQogIGFjdHVhbCA9IG9ic1YNCiAgcHJlZGljdGVkID0gb2JzQw0KICBSRUcgPSBzdW1tYXJ5KGxtKGFjdHVhbCB+IHByZWRpY3RlZCArIDApKQ0KICBNMVs0LCBpXSA9IHJvdW5kKHJtc2UoYWN0dWFsLCBwcmVkaWN0ZWQpL21lYW4oYWN0dWFsKSoxMDAsIDEpDQogIE0yWzQsIGldID0gcm91bmQocm1zZShhY3R1YWwsIHByZWRpY3RlZCksIDIpDQogIE0zWzQsIGldID0gcm91bmQoUkVHJGFkai5yLnNxdWFyZWQsIDIpDQogIE00WzQsIGldID0gcm91bmQoUkVHJGNvZWZmaWNpZW50c1sxLDFdLCAyKQ0KICANCiAgIyBTdGF0aXN0aWNhbCBkaXN0cmlidXRpb24gd2l0aCBzbGluZSBjdWJpY3MgDQogIHNvcnRfb2JzID0gc29ydChvYnNUKQ0KICBzb3J0X3NpbSA9IHNvcnQoc2ltVCkNCiAgU0QgPSBzbW9vdGguc3BsaW5lKHNvcnRfc2ltLCBzb3J0X29icykNCiAgb2JzQyA9IHByZWRpY3QoU0QsIHNpbVYpJHkNCiAgYWN0dWFsID0gb2JzVg0KICBwcmVkaWN0ZWQgPSBvYnNDDQogIFJFRyA9IHN1bW1hcnkobG0oYWN0dWFsIH4gcHJlZGljdGVkICsgMCkpDQogIE0xWzUsIGldID0gcm91bmQocm1zZShhY3R1YWwsIHByZWRpY3RlZCkvbWVhbihhY3R1YWwpKjEwMCwgMSkNCiAgTTJbNSwgaV0gPSByb3VuZChybXNlKGFjdHVhbCwgcHJlZGljdGVkKSwgMikNCiAgTTNbNSwgaV0gPSByb3VuZChSRUckYWRqLnIuc3F1YXJlZCwgMikNCiAgTTRbNSwgaV0gPSByb3VuZChSRUckY29lZmZpY2llbnRzWzEsMV0sIDIpDQogIA0KICBvdXRCQ1tbaV1dID0gYmlhcyANCiAgb3V0TFJbW2ldXSA9IExSIA0KICBvdXRRTVtbaV1dID0gUU0NCiAgb3V0U0RbW2ldXSA9IFNEDQogIA0KfQ0KDQpjb2xuYW1lcyhNMSkgPC0gY29sbmFtZXMoTTIpIDwtIGMoInRtaW4iLCAidG1heCIsICJzcmFkIikNCmNvbG5hbWVzKE00KSA8LSBjb2xuYW1lcyhNMykgPC0gYygidG1pbiIsICJ0bWF4IiwgInNyYWQiKQ0KDQpyb3duYW1lcyhNMSkgPC0gcm93bmFtZXMoTTIpIDwtIGMoIk5DIiwgIkJDIiwgIkxSIiwgIlFNIiwgIlNEIikNCnJvd25hbWVzKE0zKSA8LSByb3duYW1lcyhNNCkgPC0gYygiTkMiLCAiQkMiLCAiTFIiLCAiUU0iLCAiU0QiKQ0KDQpuYW1lcyhvdXRCQykgPC0gbmFtZXMob3V0TFIpIDwtIGMoInRtaW4iLCAidG1heCIsICJzcmFkIikNCm5hbWVzKG91dFFNKSA8LSBuYW1lcyhvdXRTRCkgPC0gYygidG1pbiIsICJ0bWF4IiwgInNyYWQiKQ0KDQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgUExPVFMNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgR2VuZXJhbCBwbG90IHNldHRpbmdzDQpwYXIob21hICAgID0gYyg0LjUsIDQuNSwgMC41LCAzKSwgICMgZ2VuZXJhbCBtYXJnaW5zDQogICAgbWZyb3cgID0gYyg0LCAzKSwgICAgICAgICAgICAgICAgIyBudW1iZXIgb2Ygc3ViLWZpZ3VyZXMNCiAgICBtYXIgICAgPSBjKDQuNSwyLjUsMS41LDEuNSksICAgICAgICAgICMgbWFyZ2lucyBwZXIgc3ViLWZpZ3VyZQ0KICAgIGZhbWlseSA9ICJzZXJpZiIsICAgICAgICAgICAgICAgICMgdGV4dCBmYW1pbHkNCiAgICBsd2QgICAgPSAxLjAsICAgICAgICAgICAgICAgICAgICAjIGxpbmUgd2lkdGgNCiAgICBsYXMgICAgPSAxLCAgICAgICAgICAgICAgICAgICAgICAjIHN0eWxlIG9mIGF4aXMgbGFiZWxzICANCiAgICBwY2ggICAgPSAxOSwgICAgICAgICAgICAgICAgICAgICAjIHBsb3R0aW5nIHBvaW50cw0KICAgIGNleCAgICA9IDAuNSwNCiAgICBwdHkgPSAicyINCikNCg0KIyBQTE9UUyBGT1IgU0lNUExFIEJJQVMgQ09SUkVDVElPTg0KdG1pbkMgPSB3ZFMkdG1pbiArIG91dEJDJHRtaW4NCnBsb3Qod2RTJHRtaW4sIHdkTyR0bWluLCB4bGltID0gYygwLDE0KSwgeWxpbSA9IGMoMCwxNCksIHhsYWIgPSAiTkFTQTogdG1pbiAoQykiLCB5bGFiID0gIk9CUzogdG1pbiAoQykiLCBjb2wgPSAiYmx1ZSIpDQpwb2ludHModG1pbkMsIHdkTyR0bWluLCBjb2wgPSAicmVkIikNCmFibGluZShjKDAsMSksIGNvbCA9ICJibGFjayIsIGx3ZCA9IDEuNSkNCg0KdG1heEMgPSB3ZFMkdG1heCArIG91dEJDJHRtYXgNCnBsb3Qod2RTJHRtYXgsIHdkTyR0bWF4LCB4bGltID0gYyg4LDI4KSwgeWxpbSA9IGMoOCwyOCksIHhsYWIgPSAiTkFTQTogdG1heCAoQykiLCB5bGFiID0gIk9CUzogdG1heCAoQykiLCBjb2wgPSAiYmx1ZSIpDQpwb2ludHModG1heEMsIHdkTyR0bWF4LCBjb2wgPSAicmVkIikNCmFibGluZShjKDAsMSksIGNvbCA9ICJibGFjayIsIGx3ZCA9IDEuNSkNCnRpdGxlKCJCaWFzcyBDb3JyZWN0aW9uIikNCg0Kc3JhZEMgPSB3ZFMkc3JhZCArIG91dEJDJHNyYWQNCnBsb3Qod2RTJHNyYWQsIHdkTyRzcmFkLCB4bGltID0gYygzLDMxKSwgeWxpbSA9IGMoMywzMSksIHhsYWIgPSAiTkFTQTogc3JhZCAoTUovbTIvZGF5KSIsIHlsYWIgPSAiT0JTOiBzcmFkIChNSi9tMi9kYXkpIiwgY29sID0gImJsdWUiKQ0KcG9pbnRzKHNyYWRDLCB3ZE8kc3JhZCwgY29sID0gInJlZCIpDQphYmxpbmUoYygwLDEpLCBjb2wgPSAiYmxhY2siLCBsd2QgPSAxLjUpDQoNCg0KIyBQTE9UUyBGT1IgTElORUFSIFJFR1JFU1NJT04NCnRtaW5DID0gcHJlZGljdChvdXRMUiR0bWluLCBuZXdkYXRhID0gZGF0YS5mcmFtZShzaW1UID0gd2RTJHRtaW4pKQ0KcGxvdCh3ZFMkdG1pbiwgd2RPJHRtaW4sIHhsaW0gPSBjKDAsMTQpLCB5bGltID0gYygwLDE0KSwgeGxhYiA9ICJOQVNBOiB0bWluIChDKSIsIHlsYWIgPSAiT0JTOiB0bWluIChDKSIsIGNvbCA9ICJibHVlIikNCnBvaW50cyh0bWluQywgd2RPJHRtaW4sIGNvbCA9ICJyZWQiKQ0KYWJsaW5lKGMoMCwxKSwgY29sID0gImJsYWNrIiwgbHdkID0gMS41KQ0KDQp0bWF4QyA9IHByZWRpY3Qob3V0TFIkdG1heCwgbmV3ZGF0YSA9IGRhdGEuZnJhbWUoc2ltVCA9IHdkUyR0bWF4KSkNCnBsb3Qod2RTJHRtYXgsIHdkTyR0bWF4LCB4bGltID0gYyg4LDI4KSwgeWxpbSA9IGMoOCwyOCksIHhsYWIgPSAiTkFTQTogdG1heCAoQykiLCB5bGFiID0gIk9CUzogdG1heCAoQykiLCBjb2wgPSAiYmx1ZSIpDQpwb2ludHModG1heEMsIHdkTyR0bWF4LCBjb2wgPSAicmVkIikNCmFibGluZShjKDAsMSksIGNvbCA9ICJibGFjayIsIGx3ZCA9IDEuNSkNCnRpdGxlKCJMaW5lYXIgUmVncmVzc2lvbiIpDQoNCnNyYWRDID0gcHJlZGljdChvdXRMUiRzcmFkLCBuZXdkYXRhID0gZGF0YS5mcmFtZShzaW1UID0gd2RTJHNyYWQpKQ0KcGxvdCh3ZFMkc3JhZCwgd2RPJHNyYWQsIHhsaW0gPSBjKDMsMzEpLCB5bGltID0gYygzLDMxKSwgeGxhYiA9ICJOQVNBOiBzcmFkIChNSi9tMi9kYXkpIiwgeWxhYiA9ICJPQlM6IHNyYWQgKE1KL20yL2RheSkiLCBjb2wgPSAiYmx1ZSIpDQpwb2ludHMoc3JhZEMsIHdkTyRzcmFkLCBjb2wgPSAicmVkIikNCmFibGluZShjKDAsMSksIGNvbCA9ICJibGFjayIsIGx3ZCA9IDEuNSkNCg0KDQojIFBMT1RTIEZPUiBRVUFOVElMRSBNQVRDSElORw0KdG1pbkMgPSBwcmVkaWN0KG91dFFNJHRtaW4sIG5ld2RhdGEgPSBkYXRhLmZyYW1lKHNDREYgPSB3ZFMkdG1pbikpDQpwbG90KHdkUyR0bWluLCB3ZE8kdG1pbiwgeGxpbSA9IGMoMCwxNCksIHlsaW0gPSBjKDAsMTQpLCB4bGFiID0gIk5BU0E6IHRtaW4gKEMpIiwgeWxhYiA9ICJPQlM6IHRtaW4gKEMpIiwgY29sID0gImJsdWUiKQ0KcG9pbnRzKHRtaW5DLCB3ZE8kdG1pbiwgY29sID0gInJlZCIpDQphYmxpbmUoYygwLDEpLCBjb2wgPSAiYmxhY2siLCBsd2QgPSAxLjUpDQoNCnRtYXhDID0gcHJlZGljdChvdXRRTSR0bWF4LCBuZXdkYXRhID0gZGF0YS5mcmFtZShzQ0RGID0gd2RTJHRtYXgpKQ0KcGxvdCh3ZFMkdG1heCwgd2RPJHRtYXgsIHhsaW0gPSBjKDgsMjgpLCB5bGltID0gYyg4LDI4KSwgeGxhYiA9ICJOQVNBOiB0bWF4IChDKSIsIHlsYWIgPSAiT0JTOiB0bWF4IChDKSIsIGNvbCA9ICJibHVlIikNCnBvaW50cyh0bWF4Qywgd2RPJHRtYXgsIGNvbCA9ICJyZWQiKQ0KYWJsaW5lKGMoMCwxKSwgY29sID0gImJsYWNrIiwgbHdkID0gMS41KQ0KdGl0bGUoIlF1YW50aWxlIE1hdGNoaW5nIikNCg0Kc3JhZEMgPSBwcmVkaWN0KG91dFFNJHNyYWQsIG5ld2RhdGEgPSBkYXRhLmZyYW1lKHNDREYgPSB3ZFMkc3JhZCkpDQpwbG90KHdkUyRzcmFkLCB3ZE8kc3JhZCwgeGxpbSA9IGMoMywzMSksIHlsaW0gPSBjKDMsMzEpLCB4bGFiID0gIk5BU0E6IHNyYWQgKE1KL20yL2RheSkiLCB5bGFiID0gIk9CUzogc3JhZCAoTUovbTIvZGF5KSIsIGNvbCA9ICJibHVlIikNCnBvaW50cyhzcmFkQywgd2RPJHNyYWQsIGNvbCA9ICJyZWQiKQ0KYWJsaW5lKGMoMCwxKSwgY29sID0gImJsYWNrIiwgbHdkID0gMS41KQ0KDQoNCiMgUExPVCBGT1IgU1RBVElTVElDQUwgRElTVFJJQlVUSU9OIFVTSU5HIFNQTElORVMNCnRtaW5DID0gcHJlZGljdChvdXRTRCR0bWluLCB3ZFMkdG1pbikkeQ0KcGxvdCh3ZFMkdG1pbiwgd2RPJHRtaW4sIHhsaW0gPSBjKDAsMTQpLCB5bGltID0gYygwLDE0KSwgeGxhYiA9ICJOQVNBOiB0bWluIChDKSIsIHlsYWIgPSAiT0JTOiB0bWluIChDKSIsIGNvbCA9ICJibHVlIikNCnBvaW50cyh0bWluQywgd2RPJHRtaW4sIGNvbCA9ICJyZWQiKQ0KYWJsaW5lKGMoMCwxKSwgY29sID0gImJsYWNrIiwgbHdkID0gMS41KQ0KDQp0bWF4QyA9IHByZWRpY3Qob3V0U0QkdG1heCwgd2RTJHRtYXgpJHkNCnBsb3Qod2RTJHRtYXgsIHdkTyR0bWF4LCB4bGltID0gYyg4LDI4KSwgeWxpbSA9IGMoOCwyOCksIHhsYWIgPSAiTkFTQTogdG1heCAoQykiLCB5bGFiID0gIk9CUzogdG1heCAoQykiLCBjb2wgPSAiYmx1ZSIpDQpwb2ludHModG1heEMsIHdkTyR0bWF4LCBjb2wgPSAicmVkIikNCmFibGluZShjKDAsMSksIGNvbCA9ICJibGFjayIsIGx3ZCA9IDEuNSkNCnRpdGxlKCJTdGF0aXN0aWNhbCBEaXN0cmlidXRpb24iKQ0KDQpzcmFkQyA9IHByZWRpY3Qob3V0U0Qkc3JhZCwgd2RTJHNyYWQpJHkNCnBsb3Qod2RTJHNyYWQsIHdkTyRzcmFkLCB4bGltID0gYygzLDMxKSwgeWxpbSA9IGMoMywzMSksIHhsYWIgPSAiTkFTQTogc3JhZCAoTUovbTIvZGF5KSIsIHlsYWIgPSAiT0JTOiBzcmFkIChNSi9tMi9kYXkpIiwgY29sID0gImJsdWUiKQ0KcG9pbnRzKHNyYWRDLCB3ZE8kc3JhZCwgY29sID0gInJlZCIpDQphYmxpbmUoYygwLDEpLCBjb2wgPSAiYmxhY2siLCBsd2QgPSAxLjUpDQoNCg0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyBGSU5BTCBXRUFUSEVSIERBVEEgRk9SIE1PREVMSU5HDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQp3byA8LSB3ZA0Kd2MgPC0gZGF0YS5mcmFtZShkYXRlID0gd2QkZGF0ZSkNCg0KIyBBcHBseSB0aGUgcXVhbnRpbGUgbWF0Y2hpbmcgdG8gZGF0YXNldA0Kd2MkdG1pbiA8LSBwcmVkaWN0KG91dFFNJHRtaW4sIG5ld2RhdGEgPSBkYXRhLmZyYW1lKHNDREYgPSB3byR0bWluKSkNCndjJHRtYXggPC0gcHJlZGljdChvdXRRTSR0bWF4LCBuZXdkYXRhID0gZGF0YS5mcmFtZShzQ0RGID0gd28kdG1heCkpDQp3YyRzcmFkIDwtIHByZWRpY3Qob3V0UU0kc3JhZCwgbmV3ZGF0YSA9IGRhdGEuZnJhbWUoc0NERiA9IHdvJHNyYWQpKQ0KDQojIEZpbmFsIHdlYXRoZXIgZGF0YQ0KV0QgPC0gd2MNCg0KYGBgDQoNCg0KIyMgU09MQU5VTSBtb2RlbCBjYWxpYnJhdGlvbg0KDQpUaGUgU09MQU5VTSBtb2RlbCB3YXMgY2FsaWJyYXRlZCB1c2luZyB0ZW1wb3JhbCBkYXRhIG9uIGNhbm9weSBjb3ZlciBhbmQgYmlvbWFzcy4gQ2Fub3B5IGNvdmVyIGRhdGEgd2FzIGZpdHRlZCB0byBhIGJldGEgZnVuY3Rpb24sIHdoaWxlIGJpb21hc3MgZGF0YSB3YXMgZml0dGVkIHRvIGEgR29tcGVydHogZnVuY3Rpb24uIE5vbmxpbmVhciByZWdyZXNzaW9uIHdhcyBhcHBsaWVkIGZvciB0aGlzIGNhbGlicmF0aW9uLiANCg0KQ2xpY2sgb24gYGNvZGVgIHRvIHZpZXcgaG93IHRoZSBjYW5vcHkgY292ZXIgZGF0YSB3YXMgZml0dGVkIHRvIHRoZSBCZXRhIGN1cnZlIHVzaW5nIHRoZSBgRml0Q3VydmVTTWAgZnVuY3Rpb24gaW4gUi4NCg0KYGBge3IgY2xhc3Muc291cmNlPSdmb2xkLWhpZGUnLCByZXN1bHRzPSdob2xkJ30NCiMgbG9hZCB0aGVybWFsIHRpbWUgZnVuY3Rpb24NCnNvdXJjZSgiLi4vUi90aGVybWFsVGltZS5SIikNCnNvdXJjZSgiLi4vUi9Tb2xhbnVtTW9kZWwuUiIpDQpzb3VyY2UoIi4uL1IvRml0Q3VydmVTTS5SIikNCg0KIyBFbWVyZ2VuY3kgZGF5DQplZSA8LSBkYXRhLmZyYW1lKEVEYXk9YygxNiwgMzMsIDM0LCAyNCwgNDIpLA0KICAgICAgICAgICAgICAgICBDT0RFPWMoIkFNQSIsICJCUkUiLCAiSFVFIiwgIlBPRCIsICJZVU4iKSkNCkVEYXkgPC0gZWUkRURheQ0KbmFtZXMoRURheSkgPC0gYygiQU1BIiwgIkJSRSIsICJIVUUiLCAiUE9EIiwgIllVTiIpDQoNCiMgUmVhZCB3ZWF0aGVyIGRhdGENCndkIDwtIHJlYWQuY3N2KCJodHRwczovL2dpdGh1Yi5jb20vam5pbmFueWEvUG90YXRvX1lpZWxkX0dhcC9yYXcvcmVmcy9oZWFkcy9tYWluL0RhdGEvZGF0YXNldF9mb3JfbW9kZWxfY2FsaWJyYXRpb24uY3N2IikNCmNvbG5hbWVzKHdkKSA8LSB0b2xvd2VyKGNvbG5hbWVzKHdkKSkNCg0KIyBUaGVybWFsIHRpbWUgY29tcHV0YXRpb24NClRUIDwtIG1hdHJpeChucm93ID0gbGVuZ3RoKHdkJGRhdGUpLCBuY29sID0gNSkNCg0KZm9yKGkgaW4gMTo1KXsNCiAgdHQgPSB0aGVybWFsVGltZShkYXRlID0gd2QkZGF0ZSwgdG1pbiA9IHdkJHRtaW4sIHRtYXggPSB3ZCR0bWF4LA0KICAgICAgICAgICAgICAgICAgIHNvd2luZyA9ICIyMDIzLTEwLTI0IiwgZW5kSGFydmVzdCA9ICIyMDI0LTA0LTE4IiwNCiAgICAgICAgICAgICAgICAgICBFbWVyZ2VuY3lEYXlzID0gRURheVtpXSkNCiAgVFRbLGldID0gcm91bmQodHQkdHQsMSkNCn0NCg0KY29sbmFtZXMoVFQpIDwtIG5hbWVzKEVEYXkpDQpyb3duYW1lcyhUVCkgPC0gMDoobGVuZ3RoKHdkJGRhdGUpLTEpDQojaGVhZChUVCkNCg0KVFQgPC0gZGF0YS5mcmFtZShkYXRlID0gd2QkZGF0ZSwgZGFwID0gMDoobGVuZ3RoKHdkJGRhdGUpLTEpLCBUVCkNCg0KIyBUVCAgcGVyIHZhcmlldHkNCm91dENEIDwtIGFzLmRhdGEuZnJhbWUob3V0Q0QpDQpUVF9DRCA8LSBUVFtUVCRkYXAgJWluJSBvdXRDRCREQVAsIDI6N10NCg0KIyMjIGZ1bmN0aW9uIHRvIGNhbGlicmF0ZSB0aGUgbW9kZWwNCnRtIDwtIHZlY3RvcigpDQp0ZSA8LSB2ZWN0b3IoKQ0Kd21heCA8LSB2ZWN0b3IoKQ0KY2MgPC0gbGlzdCgpDQoNCmZvcihpIGluIDE6NSl7DQogIHggPC0gVFRfQ0RbLCAxK2ldDQogIHkgPC0gb3V0Q0RbLCAxK2ldDQogIGZpdHQuY2MgPC0gRml0Q3VydmVTTSh4LCB5LCB4ZnVuID0gIkJldGEiLCB4dGltZSA9ICJ0dCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgaW5pdC5wYXIgPSBjKDEwMCwgNTAwLCAwLjkpICwgdXNlLnBhci5kZWZhdWx0ID0gRkFMU0UpDQogIA0KICB0bVtpXSA8LSBmaXR0LmNjJHBhcmFtZXRlcnNbMV0NCiAgdGVbaV0gPC0gZml0dC5jYyRwYXJhbWV0ZXJzWzJdDQogIHdtYXhbaV0gPC0gZml0dC5jYyRwYXJhbWV0ZXJzWzNdDQogIA0KICBjY1tbaV1dIDwtIGZpdHQuY2MNCn0NCg0KIyBDcm9wIHBhcmFtZXRlcnMgZm9yIHRoZSBCZXRhIGZ1bmN0aW9uDQpuYW1lcyh0bSkgPC0gbmFtZXModGUpIDwtIG5hbWVzKHdtYXgpIDwtIGMoIkFNQSIsICJCUkUiLCAiSFVFIiwgIlBPRCIsICJZVU4iKQ0KbmFtZXMoY2MpIDwtIGMoIkFNQSIsICJCUkUiLCAiSFVFIiwgIlBPRCIsICJZVU4iKQ0KDQojIyMjIyMjIyMjIyMzDQojIyMjIyMjIyMjIyMjDQojIyMjIyMjIyMjIyMNCg0KcGFyKG1mcm93ID0gYygyLDMpLA0KICAgIG1hciA9IGMoMi41LDMuNSwyLjUsMC41KSwNCiAgICBsYXMgPSAxLCBwdHkgPSAicyIsIGNleCA9IDEuKQ0KIyBQTE9UUyBDQyBBTUFSSUxJUw0KcGxvdChjYyRBTUEkc2ltdWxhdGVkLmRhdGEkdGltZSwgY2MkQU1BJHNpbXVsYXRlZC5kYXRhJHNpbXVsYXRlZF9kYXRhLA0KICAgICB4bGFiID0gInRoZXJtYWwgdGltZSAoQypkYXkpIiwgeWxhYiA9ICJjYW5vcHkgY292ZXIgKCUpIiwgbGFzID0gMSwgeWxpbSA9IGMoMCwxKSwNCiAgICAgdHlwZSA9ICJsIiwgbHdkID0gMS41LCBtYWluID0gIkFtYXJpbGlzIiwgeGxpbSA9IGMoMCwxNDAwKSkNCnBvaW50cyhjYyRBTUEkZml0dGVkLmRhdGEkdGltZSwgY2MkQU1BJGZpdHRlZC5kYXRhJG9icy8xMDAsIGNvbCA9ICJyZWQiLCBsd2QgPSAxLjIsIGNleCA9IDEuNSkNCg0KIyBQTE9UUyBDQyBCUkVUQU5BDQpwbG90KGNjJEJSRSRzaW11bGF0ZWQuZGF0YSR0aW1lLCBjYyRCUkUkc2ltdWxhdGVkLmRhdGEkc2ltdWxhdGVkX2RhdGEsDQogICAgIHhsYWIgPSAidGhlcm1hbCB0aW1lIChDKmRheSkiLCB5bGFiID0gImNhbm9weSBjb3ZlciAoJSkiLCBsYXMgPSAxLCB5bGltID0gYygwLDEpLA0KICAgICB0eXBlID0gImwiLCBsd2QgPSAxLjUsIG1haW4gPSAiQnJldGHDsWEiLCB4bGltID0gYygwLDE0MDApKQ0KcG9pbnRzKGNjJEJSRSRmaXR0ZWQuZGF0YSR0aW1lLCBjYyRCUkUkZml0dGVkLmRhdGEkb2JzLzEwMCwgY29sID0gInJlZCIsIGx3ZCA9IDEuMiwgY2V4ID0gMS41KQ0KDQojIFBMT1RTIENDIEhVRVZPIERFIElORElPDQpwbG90KGNjJEhVRSRzaW11bGF0ZWQuZGF0YSR0aW1lLCBjYyRIVUUkc2ltdWxhdGVkLmRhdGEkc2ltdWxhdGVkX2RhdGEsDQogICAgIHhsYWIgPSAidGhlcm1hbCB0aW1lIChDKmRheSkiLCB5bGFiID0gImNhbm9weSBjb3ZlciAoJSkiLCBsYXMgPSAxLCB5bGltID0gYygwLDEpLA0KICAgICB0eXBlID0gImwiLCBsd2QgPSAxLjUsIG1haW4gPSAiSHVldm8gZGUgSW5kaW8iLCB4bGltID0gYygwLDE0MDApKQ0KcG9pbnRzKGNjJEhVRSRmaXR0ZWQuZGF0YSR0aW1lLCBjYyRIVUUkZml0dGVkLmRhdGEkb2JzLzEwMCwgY29sID0gInJlZCIsIGx3ZCA9IDEuMiwgY2V4ID0gMS41KQ0KDQojIFBMT1RTIENDIFBPREVST1NBDQpwbG90KGNjJFBPRCRzaW11bGF0ZWQuZGF0YSR0aW1lLCBjYyRQT0Qkc2ltdWxhdGVkLmRhdGEkc2ltdWxhdGVkX2RhdGEsDQogICAgIHhsYWIgPSAidGhlcm1hbCB0aW1lIChDKmRheSkiLCB5bGFiID0gImNhbm9weSBjb3ZlciAoJSkiLCBsYXMgPSAxLCB5bGltID0gYygwLDEpLA0KICAgICB0eXBlID0gImwiLCBsd2QgPSAxLjUsIG1haW4gPSAiUG9kZXJvc2EiLCB4bGltID0gYygwLDE0MDApKQ0KcG9pbnRzKGNjJFBPRCRmaXR0ZWQuZGF0YSR0aW1lLCBjYyRQT0QkZml0dGVkLmRhdGEkb2JzLzEwMCwgY29sID0gInJlZCIsIGx3ZCA9IDEuMiwgY2V4ID0gMS41KQ0KDQojIFBMT1RTIENDIFlVTkdBWQ0KcGxvdChjYyRZVU4kc2ltdWxhdGVkLmRhdGEkdGltZSwgY2MkWVVOJHNpbXVsYXRlZC5kYXRhJHNpbXVsYXRlZF9kYXRhLA0KICAgICB4bGFiID0gInRoZXJtYWwgdGltZSAoQypkYXkpIiwgeWxhYiA9ICJjYW5vcHkgY292ZXIgKCUpIiwgbGFzID0gMSwgeWxpbSA9IGMoMCwxKSwNCiAgICAgdHlwZSA9ICJsIiwgbHdkID0gMS41LCBtYWluID0gIll1bmdheSIsIHhsaW0gPSBjKDAsMTQwMCkpDQpwb2ludHMoY2MkWVVOJGZpdHRlZC5kYXRhJHRpbWUsIGNjJFlVTiRmaXR0ZWQuZGF0YSRvYnMvMTAwLCBjb2wgPSAicmVkIiwgbHdkID0gMS4yLCBjZXggPSAxLjUpDQoNCmBgYA0KDQpDbGljayBvbiBgY29kZWAgdG8gdmlldyBob3cgdGhlIGhhcnZlc3QgaW5kZXggKHR1YmVyL3RvdGFsIGJpb21hc3MpIGRhdGEgd2FzIGZpdHRlZCB0byB0aGUgR29tcGVydHogY3VydmUgdXNpbmcgdGhlIGBGaXRDdXJ2ZVNNYCBmdW5jdGlvbiBpbiBSLg0KDQpgYGB7ciBjbGFzcy5zb3VyY2U9J2ZvbGQtaGlkZScsIHJlc3VsdHM9J2hvbGQnfQ0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyBCSU9NQVNTIA0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMjIGRhdGVzIA0Kb3V0QkQgPC0gYXMuZGF0YS5mcmFtZShvdXRCRCkNCm91dEJEJERBUFs2XSA8LSAxNzcNCnplcm9zX3JvdyA8LSByZXAoMCwgNikNCmxhc3Rfcm93IDwtIGMoMTg1LCBhcHBseShvdXRCRFssMjo2XSwgMiwgbWF4LCBuYS5ybT1UUlVFKSkNCm91dEJEX25ldyA8LSByYmluZCh6ZXJvc19yb3csIG91dEJEKQ0Kb3V0QkRfbmV3IDwtIHJiaW5kKG91dEJEX25ldywgbGFzdF9yb3cpDQojb3V0QkRfbmV3JEFNQVs3XSA8LSBvdXRCRF9uZXckQU1BWzhdDQojb3V0QkRfbmV3JEFNQVs4XSA8LSBOQQ0KDQpUVF9CRCA8LSBUVFtUVCRkYXAgJWluJSBvdXRCRCREQVAsIDI6N10NCnplcm9zX3JvdyA8LSByZXAoMCwgNikNCmxhc3Rfcm93IDwtIGMoMTg1LCB1bmxpc3QoVFRfQkRbNiwyOjZdKSArIDE1MCkNClRUX0JEX25ldyA8LSByYmluZCh6ZXJvc19yb3csIFRUX0JEKQ0KVFRfQkRfbmV3IDwtIHJiaW5kKFRUX0JEX25ldywgbGFzdF9yb3cpDQoNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KYiA8LSB2ZWN0b3IoKQ0KdHUgPC0gdmVjdG9yKCkNCkEgPC0gdmVjdG9yKCkNCmhoIDwtIGxpc3QoKQ0KDQpmb3IoaSBpbiAxOjUpew0KICB4IDwtIFRUX0JEWywgMStpXQ0KICB5IDwtIG91dEJEWywgMStpXQ0KICANCiAgZml0dC5oaCA8LSBGaXRDdXJ2ZVNNKHgsIHksIHhmdW4gPSAiR29tcGVydHoiLCB4dGltZSA9ICJ0dCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgaW5pdC5wYXIgPSBjKDI1MCwgNjAwLCAwLjkpICwgdXNlLnBhci5kZWZhdWx0ID0gRkFMU0UpDQogIA0KICBiW2ldIDwtIGZpdHQuaGgkcGFyYW1ldGVyc1sxXQ0KICB0dVtpXSA8LSBmaXR0LmhoJHBhcmFtZXRlcnNbMl0NCiAgQVtpXSA8LSBmaXR0LmhoJHBhcmFtZXRlcnNbM10NCiAgDQogIGhoW1tpXV0gPC0gZml0dC5oaA0KfQ0KDQpuYW1lcyhiKSA8LSBuYW1lcyh0dSkgPC0gbmFtZXMoQSkgPC0gYygiQU1BIiwgIkJSRSIsICJIVUUiLCAiUE9EIiwgIllVTiIpDQpuYW1lcyhoaCkgPC0gYygiQU1BIiwgIkJSRSIsICJIVUUiLCAiUE9EIiwgIllVTiIpDQoNCg0KIyMjIyMjIyMjIyMjMw0KIyMjIyMjIyMjIyMjIw0KIyMjIyMjIyMjIyMjDQoNCnBhcihtZnJvdyA9IGMoMiwzKSwNCiAgICBtYXIgPSBjKDIuNSwzLjUsMi41LDAuNSksDQogICAgbGFzID0gMSwgcHR5ID0gInMiLCBjZXggPSAxLikNCiMgUExPVFMgSEkgQU1BUklMSVMNCnBsb3QoaGgkQU1BJHNpbXVsYXRlZC5kYXRhJHRpbWUsIGhoJEFNQSRzaW11bGF0ZWQuZGF0YSRzaW11bGF0ZWRfZGF0YSwNCiAgICAgeGxhYiA9ICJ0aGVybWFsIHRpbWUgKEMqZGF5KSIsIHlsYWIgPSAiaGFydmVzdCBpbmRleCAoJSkiLCBsYXMgPSAxLCB5bGltID0gYygwLDEpLA0KICAgICB0eXBlID0gImwiLCBsd2QgPSAxLjUsIG1haW4gPSAiQW1hcmlsaXMiLCB4bGltID0gYygwLDE3MDApKQ0KcG9pbnRzKGhoJEFNQSRmaXR0ZWQuZGF0YSR0aW1lLCBoaCRBTUEkZml0dGVkLmRhdGEkb2JzLCBjb2wgPSAicmVkIiwgbHdkID0gMS4yLCBjZXggPSAxLjUpDQoNCiMgUExPVFMgSEkgQlJFVEFOQQ0KcGxvdChoaCRCUkUkc2ltdWxhdGVkLmRhdGEkdGltZSwgaGgkQlJFJHNpbXVsYXRlZC5kYXRhJHNpbXVsYXRlZF9kYXRhLA0KICAgICB4bGFiID0gInRoZXJtYWwgdGltZSAoQypkYXkpIiwgeWxhYiA9ICJoYXJ2ZXN0IGluZGV4ICglKSIsIGxhcyA9IDEsIHlsaW0gPSBjKDAsMSksDQogICAgIHR5cGUgPSAibCIsIGx3ZCA9IDEuNSwgbWFpbiA9ICJCcmV0YcOxYSIsIHhsaW0gPSBjKDAsMTcwMCkpDQpwb2ludHMoaGgkQlJFJGZpdHRlZC5kYXRhJHRpbWUsIGhoJEJSRSRmaXR0ZWQuZGF0YSRvYnMsIGNvbCA9ICJyZWQiLCBsd2QgPSAxLjIsIGNleCA9IDEuNSkNCg0KIyBQTE9UUyBDQyBIVUVWTyBERSBJTkRJTw0KcGxvdChoaCRIVUUkc2ltdWxhdGVkLmRhdGEkdGltZSwgaGgkSFVFJHNpbXVsYXRlZC5kYXRhJHNpbXVsYXRlZF9kYXRhLA0KICAgICB4bGFiID0gInRoZXJtYWwgdGltZSAoQypkYXkpIiwgeWxhYiA9ICJoYXJ2ZXN0IGluZGV4ICglKSIsIGxhcyA9IDEsIHlsaW0gPSBjKDAsMSksDQogICAgIHR5cGUgPSAibCIsIGx3ZCA9IDEuNSwgbWFpbiA9ICJIdWV2byBkZSBJbmRpbyIsIHhsaW0gPSBjKDAsMTcwMCkpDQpwb2ludHMoaGgkSFVFJGZpdHRlZC5kYXRhJHRpbWUsIGhoJEhVRSRmaXR0ZWQuZGF0YSRvYnMsIGNvbCA9ICJyZWQiLCBsd2QgPSAxLjIsIGNleCA9IDEuNSkNCg0KIyBQTE9UUyBDQyBQT0RFUk9TQQ0KcGxvdChoaCRQT0Qkc2ltdWxhdGVkLmRhdGEkdGltZSwgaGgkUE9EJHNpbXVsYXRlZC5kYXRhJHNpbXVsYXRlZF9kYXRhLA0KICAgICB4bGFiID0gInRoZXJtYWwgdGltZSAoQypkYXkpIiwgeWxhYiA9ICJoYXJ2ZXN0IGluZGV4ICglKSIsIGxhcyA9IDEsIHlsaW0gPSBjKDAsMSksDQogICAgIHR5cGUgPSAibCIsIGx3ZCA9IDEuNSwgbWFpbiA9ICJQb2Rlcm9zYSIsIHhsaW0gPSBjKDAsMTcwMCkpDQpwb2ludHMoaGgkUE9EJGZpdHRlZC5kYXRhJHRpbWUsIGhoJFBPRCRmaXR0ZWQuZGF0YSRvYnMsIGNvbCA9ICJyZWQiLCBsd2QgPSAxLjIsIGNleCA9IDEuNSkNCg0KIyBQTE9UUyBDQyBZVU5HQVkNCnBsb3QoaGgkWVVOJHNpbXVsYXRlZC5kYXRhJHRpbWUsIGhoJFlVTiRzaW11bGF0ZWQuZGF0YSRzaW11bGF0ZWRfZGF0YSwNCiAgICAgeGxhYiA9ICJ0aGVybWFsIHRpbWUgKEMqZGF5KSIsIHlsYWIgPSAiaGFydmVzdCBpbmRleCAoJSkiLCBsYXMgPSAxLCB5bGltID0gYygwLDEpLA0KICAgICB0eXBlID0gImwiLCBsd2QgPSAxLjUsIG1haW4gPSAiWXVuZ2F5IiwgeGxpbSA9IGMoMCwxNzAwKSkNCnBvaW50cyhoaCRZVU4kZml0dGVkLmRhdGEkdGltZSwgaGgkWVVOJGZpdHRlZC5kYXRhJG9icywgY29sID0gInJlZCIsIGx3ZCA9IDEuMiwgY2V4ID0gMS41KQ0KDQoNCiMjIyMjIyMjIyMjIyMjIyMjIw0KDQp0bS5kYXAgPC0gdmVjdG9yKCkNCnRlLmRhcCA8LSB2ZWN0b3IoKQ0Kd21heC5kYXAgPC0gdmVjdG9yKCkNCg0KZm9yKGkgaW4gMTo1KXsNCiAgeCA8LSBUVF9DRCRkYXANCiAgeSA8LSBvdXRDRFssIDEraV0NCiAgZml0dC5jYyA8LSBGaXRDdXJ2ZVNNKHgsIHksIHhmdW4gPSAiQmV0YSIsIHh0aW1lID0gImRhcCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgaW5pdC5wYXIgPSBjKDQwLCAxMjAsIDAuOSkgLCB1c2UucGFyLmRlZmF1bHQgPSBGQUxTRSkNCiAgDQogIHRtLmRhcFtpXSA8LSBmaXR0LmNjJHBhcmFtZXRlcnNbMV0NCiAgdGUuZGFwW2ldIDwtIGZpdHQuY2MkcGFyYW1ldGVyc1syXQ0KICB3bWF4LmRhcFtpXSA8LSBmaXR0LmNjJHBhcmFtZXRlcnNbM10NCiAgDQp9DQoNCm5hbWVzKHRtLmRhcCkgPC0gbmFtZXModGUuZGFwKSA8LSBuYW1lcyh3bWF4LmRhcCkgPC0gYygiQU1BIiwgIkJSRSIsICJIVUUiLCAiUE9EIiwgIllVTiIpDQoNCg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiMgYmV0YSBmdW5jdGlvbg0KZl9iZXRhIDwtIGZ1bmN0aW9uKHQsIHRtLCB0ZSwgd21heCl7DQogIGNjPXdtYXgqKDErKHRlLXQpLyh0ZS10bSkpKigodC90ZSleKHRlLyh0ZS10bSkpKQ0KICBjYz1pZmVsc2UoY2M+MCwgY2MsIDApDQp9DQoNCiMgZ29tcGVydHogZnVuY3Rpb24NCmZfZ29tcGVydHogPC0gZnVuY3Rpb24odCwgYiwgdHUsIEEpew0KICBBKihleHAoLWV4cCgtKDEvYikqKHQtdHUpKSkpDQp9DQoNCg0KIyMjIENDIGRhaWx5IGJhc2lzDQp2YXIgPC0gYygiQU1BIiwgIkJSRSIsICJIVUUiLCAiUE9EIiwgIllVTiIpDQoNCndkJGRhcCA8LSAwOihsZW5ndGgod2QkZGF0ZSktMSkNCndkJGNjQU1BIDwtIGZfYmV0YSh3ZCRkYXAsIHRtLmRhcFsiQU1BIl0sIHRlLmRhcFsiQU1BIl0sIHdtYXguZGFwWyJBTUEiXSkNCndkJGNjQlJFIDwtIGZfYmV0YSh3ZCRkYXAsIHRtLmRhcFsiQlJFIl0sIHRlLmRhcFsiQlJFIl0sIHdtYXguZGFwWyJCUkUiXSkNCndkJGNjSFVFIDwtIGZfYmV0YSh3ZCRkYXAsIHRtLmRhcFsiSFVFIl0sIHRlLmRhcFsiSFVFIl0sIHdtYXguZGFwWyJIVUUiXSkNCndkJGNjUE9EIDwtIGZfYmV0YSh3ZCRkYXAsIHRtLmRhcFsiUE9EIl0sIHRlLmRhcFsiUE9EIl0sIHdtYXguZGFwWyJQT0QiXSkNCndkJGNjWVVOIDwtIGZfYmV0YSh3ZCRkYXAsIHRtLmRhcFsiWVVOIl0sIHRlLmRhcFsiWVVOIl0sIHdtYXguZGFwWyJZVU4iXSkNCg0Kd2QkcGFyIDwtIHdkJHNyYWQqMC40OA0Kd2QkaW50QU1BIDwtIHdkJHNyYWQqd2QkY2NBTUENCndkJGludEJSRSA8LSB3ZCRzcmFkKndkJGNjQlJFDQp3ZCRpbnRIVUUgPC0gd2Qkc3JhZCp3ZCRjY0hVRQ0Kd2QkaW50UE9EIDwtIHdkJHNyYWQqd2QkY2NQT0QNCndkJGludFlVTiA8LSB3ZCRzcmFkKndkJGNjWVVODQoNCndkJGN1bUFNQSA8LSBjdW1zdW0od2QkaW50QU1BKQ0Kd2QkY3VtQlJFIDwtIGN1bXN1bSh3ZCRpbnRCUkUpDQp3ZCRjdW1IVUUgPC0gY3Vtc3VtKHdkJGludEhVRSkNCndkJGN1bVBPRCA8LSBjdW1zdW0od2QkaW50UE9EKQ0Kd2QkY3VtWVVOIDwtIGN1bXN1bSh3ZCRpbnRZVU4pDQoNCkNVTSA8LSBkYXRhLmZyYW1lKEFNQSA9IHdkJGN1bUFNQVtUVF9CRCRkYXBdLA0KICAgICAgICAgICAgICAgICAgQlJFID0gd2QkY3VtQlJFW1RUX0JEJGRhcF0sDQogICAgICAgICAgICAgICAgICBIVUUgPSB3ZCRjdW1IVUVbVFRfQkQkZGFwXSwNCiAgICAgICAgICAgICAgICAgIFBPRCA9IHdkJGN1bVBPRFtUVF9CRCRkYXBdLA0KICAgICAgICAgICAgICAgICAgWVVOID0gd2QkY3VtWVVOW1RUX0JEJGRhcF0pDQoNCg0KUlVFIDwtIGMoc3VtbWFyeShsbShvdXRURE0kQU1BKjEwMCB+IENVTSRBTUEpKSRjb2VmZmljaWVudHNbMl0sDQogICAgICAgICBzdW1tYXJ5KGxtKG91dFRETSRCUkUqMTAwIH4gQ1VNJEJSRSkpJGNvZWZmaWNpZW50c1syXSwNCiAgICAgICAgIHN1bW1hcnkobG0ob3V0VERNJEhVRSoxMDAgfiBDVU0kSFVFKSkkY29lZmZpY2llbnRzWzJdLA0KICAgICAgICAgc3VtbWFyeShsbShvdXRURE0kUE9EKjEwMCB+IENVTSRQT0QpKSRjb2VmZmljaWVudHNbMl0sDQogICAgICAgICBzdW1tYXJ5KGxtKG91dFRETSRZVU4qMTAwIH4gQ1VNJFlVTikpJGNvZWZmaWNpZW50c1syXSkNCm5hbWVzKFJVRSkgPC0gdmFyDQoNCkRNQy5iY2sgPC0gb3V0RE1DIA0KRE1DID0gYXBwbHkoRE1DLmJja1ssMjo2XSwyLG1heCxuYS5ybT1UUlVFKQ0KDQpWQVIgPSBuYW1lcyhETUMpDQpwYXJhbXMgPC0gZGF0YS5mcmFtZShWQVIsIA0KICAgICAgICAgICAgICAgICAgICAgcm91bmQodGUsMSksIHJvdW5kKHRtLDEpLCByb3VuZCh3bWF4LDIpLCANCiAgICAgICAgICAgICAgICAgICAgIHJvdW5kKGIsMSksIHJvdW5kKHR1LDEpLCByb3VuZChBLDIpLA0KICAgICAgICAgICAgICAgICAgICAgcm91bmQoUlVFLDIpLCByb3VuZChETUMsMikpDQoNCmNvbG5hbWVzKHBhcmFtcyk8LSBjKCJDT0RFIiwidGUiLCJ0bSIsIndtYXgiLA0KICAgICAgICAgICAgICAgICAgICAgImIiLCJ0dSIsIkEiLCJSVUUiLCJETUMiKQ0Kcm93bmFtZXMocGFyYW1zKSA8LSAxOjUNCg0KYGBgDQoNClRoZSBjcm9wIHBhcmFtZXRlcnMgb2YgdGhlIFNPTEFOVU0gbW9kZWwgd2VyZSBkZXRlcm1pbmVkIGZvciBlYWNoIHZhcmlldHkuICANCg0KYGBge3IgZWNobz1GQUxTRSwgcmVzdWx0cz0nYXNpcyd9DQp5MSA8LSBjKCJ0aGVybWFsIHRpbWUgYXQgdGhlIG1heGltdW0gZ3Jvd3RoIHJhdGUgKHRlKSIsInRoZXJtYWwgdGltZSBhdCB0aGUgbWF4aW11bSBjYW5vcHkgY292ZXIgKHRtKSIsIm1heGltdW0gY2Fub3B5IGNvdmVyICh3bWF4KSIsInRoZXJtYWwgdGltZSBhdCB0aGUgaW5pdGlhbCBzbG9wZSBvZiBwYXJ0aXRpb24gY3VydmUgKGIpIiwidGhlcm1hbCB0aW1lIGF0IHRoZSBtYXhpbXVtIGdyb3d0aCByYXRlIG9mIHBhcnRpdGlvbiBjdXJ2ZSAodHUpIiwibWF4aW11bSBoYXJ2ZXN0IGluZGV4IChBKSIsInJhZGlhdGlvbiB1c2UgZWZmaWNpZW5jeSAoUlVFKSIsImRyeSBtYXR0ZXIgY29uY2VudHJhdGlvbiBvZiB0dWJlcnMgKERNQykiKQ0KeTIgPC0gYygiOTczLjIiLCAiNDgxLjIiLCAiMC45NiIsICIyNzMuMiIsICI1NDcuNiIsICIwLjkwIiwgIjEuMjAiLCAiMC4xNyIpDQp5MyA8LSBjKCI5NDYuOCIsICIzMTQuNyIsICIwLjk1IiwgIjI1Ni43IiwgIjcwMC44IiwgIjAuNzYiLCAiMS4xMSIsICIwLjI0IikNCnk0IDwtIGMoIjkwNC45IiwgIjM3MS42IiwgIjAuNzkiLCAiMjY5LjciLCAiNjYyLjYiLCAiMC44OSIsICIxLjI4IiwgIjAuMjQiKQ0KeTUgPC0gYygiODk1LjEiLCAiMzUwLjEiLCAiMS4wMCIsICIyMDQuMCIsICI4MTYuNCIsICIwLjgwIiwgIjEuNjkiLCAiMC4yMyIpDQp5NiA8LSBjKCI3ODQuMCIsICIyMjIuNCIsICIwLjg4IiwgIjI5NC42IiwgIjYyOC4xIiwgIjAuOTAiLCAiMS44MiIsICIwLjE4IikNCg0KDQp0YiA8LSBkYXRhLmZyYW1lKHkxLCB5MiwgeTMsIHk0LCB5NSwgeTYpDQpjb2xuYW1lcyh0YikgPC0gYygiUEFSQU1FVEVSIiwgIkFNQVJJTElTIiwgIkJSRVRBTkEiLCAiSFVFVk8gREUgSU5ESU8iLCAiUE9ERVJPU0EiLCAiWVVOR0FZIikNCg0Ka25pdHI6OmthYmxlKHRiLCBjYXB0aW9uID0gIlRhYmxlIDEuIENyb3AgcGFyYW1ldGVycyBvZiB0aGUgU09MQU5VTSBtb2RlbCBmb3IgdGhlIDUgbW9zdCBjb21tb24gdmFyaWV0aWVzIHVzZWQgaW4gQ2h1Z2F5LCBMYSBMaWJlcnRhZCwgaW4gUGVydS4iKQ0KDQpgYGANCg0KDQojIyBEZXRlcm1pbmF0aW9uIG9mIG9wdGltdW0gcGxhbnRpbmcgZGF0ZQ0KDQpEZXRlcm1pbmF0aW9uIG9mIG9wdGltdW0gcGxhbnRpbmcgZGF0ZSBpbnZvbHZlcyBpZGVudGlmeWluZyB0aGUgYmVzdCB0aW1lIHRvIHBsYW50IGEgY3JvcCB0byBtYXhpbWl6ZSB5aWVsZCBhbmQgbWluaW1pemUgcmlza3MgZnJvbSBhZHZlcnNlIHdlYXRoZXIgY29uZGl0aW9ucywgcGVzdHMsIG9yIGRpc2Vhc2VzLiBUaGlzIHByb2Nlc3MgY29uc2lkZXJzIGZhY3RvcnMgc3VjaCBhcyB0ZW1wZXJhdHVyZSwgcmFpbmZhbGwsIGFuZCBkYXkgbGVuZ3RoIHRvIGVuc3VyZSBvcHRpbWFsIGdyb3d0aCBjb25kaXRpb25zLiBGb3IgdGhpcywgZGF0YSBmcm9tIHRoZSBJbnRlZ3JhdGVkIEFncmljdWx0dXJhbCBTdGF0aXN0aWNzIFN5c3RlbSB3YXMgdXNlZCB0byBzdXBwb3J0IHRoZSBhbmFseXNpcyBhbmQgaWRlbnRpZnkgdGhlIG1vc3Qgc3VpdGFibGUgcGxhbnRpbmcgZGF0ZXMgYmFzZWQgb24gaGlzdG9yaWNhbCBhZ3JpY3VsdHVyYWwgYW5kIGNsaW1hdGljIHRyZW5kcy4NCg0KYGBge3IgY2xhc3Muc291cmNlPSdmb2xkLWhpZGUnLCByZXN1bHRzPSdob2xkJ30NCnMxIDwtIHJlYWQuY3N2KCIuLi9EYXRhL291dF9uYXRpb25hbF9wb3RhdG9fZGF0YS5jc3YiKQ0KDQojIGRhdGEgZm9yIExhIExpYmVydGFkDQpMTCA8LSBzMVtzMSRERVBBUlRBTUVOVE8gPT0gIkxBIExJQkVSVEFEIiwgXQ0KI3N1bW1hcnkoTEwpDQpMTCRZWVlZIDwtIExMJEFOSU8NCkxMJE1NIDwtIExMJE1FUw0KDQojIGNoZWNrIENPU0VDSEE9PTAgJiBQUk9EVUNDSU9OPT0wDQojc3VtKExMJFBST0RVQ0NJT05bTEwkQ09TRUNIQSA9PSAwXSkNCiNzdW0oTEwkQ09TRUNIQVtMTCRQUk9EVUNUTyA9PSAwXSkNCg0KIyBtb250bHkgc3VtbWFyeSBmb3IgTEwNCnNtckxMLk0gPC0gTEwgJT4lDQogIGdyb3VwX2J5KFlZWVksTU0pICU+JQ0KICBzdW1tYXJpc2UoU0lFTUJSQSA9IHN1bShTSUVNQlJBKSwNCiAgICAgICAgICAgIENPU0VDSEEgPSBzdW0oQ09TRUNIQSksDQogICAgICAgICAgICBQUk9EVUNDSU9OID0gc3VtKFBST0RVQ0NJT04pKQ0KDQojIHllYXJseSBzdW1tYXJ5IGZvciBMTA0Kc21yTEwuWSA8LSBMTCAlPiUNCiAgZ3JvdXBfYnkoWVlZWSkgJT4lDQogIHN1bW1hcmlzZShTSUVNQlJBID0gc3VtKFNJRU1CUkEpLA0KICAgICAgICAgICAgQ09TRUNIQSA9IHN1bShDT1NFQ0hBKSwNCiAgICAgICAgICAgIFBST0RVQ0NJT04gPSBzdW0oUFJPRFVDQ0lPTikpDQoNCiMgeWllbGQgZXN0aW1hdGlvbg0Kc21yTEwuWSA8LSBhcy5kYXRhLmZyYW1lKHNtckxMLlkpDQpzbXJMTC5ZJFJFTkRJTUlFTlRPIDwtIHNtckxMLlkkUFJPRFVDQ0lPTi9zbXJMTC5ZJENPU0VDSEENCg0KIyBkYXRhIGZvciBDaHVnYXkNCkNIIDwtIExMW0xMJFBST1ZJTkNJQSA9PSAiU0FOQ0hFWiBDQVJSSU9OIiAmIExMJERJU1RSSVRPID09ICJDSFVHQVkiLCBdDQojc3VtbWFyeShDSCkNCg0KIyBtb250bHkgc3VtbWFyeSBmb3IgTEwNCnNtckNILk0gPC0gQ0ggJT4lDQogIGdyb3VwX2J5KFlZWVksTU0pICU+JQ0KICBzdW1tYXJpc2UoU0lFTUJSQSA9IHN1bShTSUVNQlJBKSwNCiAgICAgICAgICAgIENPU0VDSEEgPSBzdW0oQ09TRUNIQSksDQogICAgICAgICAgICBQUk9EVUNDSU9OID0gc3VtKFBST0RVQ0NJT04pKQ0KDQojIGRhdGEgZm9yIGZpZ3VyZQ0KbiA8LSBsZW5ndGgoc21yQ0guTSRZWVlZKQ0KWWVhciA8LSBjKHNtckNILk0kWVlZWSwgc21yQ0guTSRZWVlZKQ0KTW9udGggPC0gYyhzbXJDSC5NJE1NLCBzbXJDSC5NJE1NKQ0KQXJlYSA8LSBjKHNtckNILk0kU0lFTUJSQSwgc21yQ0guTSRDT1NFQ0hBKQ0KVHlwZSA8LSBjKHJlcCgiUGxhbnRlZCIsIG4pLCByZXAoIkhhcnZlc3RlZCIsIG4pKQ0KDQpkZiA8LSBkYXRhLmZyYW1lKFllYXIsIE1vbnRoLCBBcmVhLCBUeXBlKQ0KZGYkWWVhciA8LSBhcy5mYWN0b3IoWWVhcikNCg0KIyBtZWFuIHZhbHVlcw0KbWVhbl92YWx1ZXMgPC0gZGYgJT4lDQogIGdyb3VwX2J5KE1vbnRoLCBUeXBlKSAlPiUNCiAgc3VtbWFyaXplKG1lYW5fYXJlYSA9IG1lYW4oQXJlYSwgbmEucm0gPSBUUlVFKSkNCg0KIyBkZWZpbmUgZG91YmxlIGdhdXNzaWFuIGZ1bmN0aW9uDQpkb3VibGVfZ2F1c3NpYW4gPC0gZnVuY3Rpb24oeCwgYTEsIGIxLCBjMSwgYTIsIGIyLCBjMikgew0KICBhMSAqIGV4cCgtKHggLSBiMSleMiAvICgyICogYzFeMikpICsgYTIgKiBleHAoLSh4IC0gYjIpXjIgLyAoMiAqIGMyXjIpKQ0KfQ0KDQojIGZpdCBjdXJ2ZQ0KZml0X21vZGVscyA8LSBtZWFuX3ZhbHVlcyAlPiUNCiAgZ3JvdXBfYnkoVHlwZSkgJT4lDQogIGRvKGZpdCA9IG5sc0xNKG1lYW5fYXJlYSB+IGRvdWJsZV9nYXVzc2lhbihNb250aCwgYTEsIGIxLCBjMSwgYTIsIGIyLCBjMiksDQogICAgICAgICAgICAgICAgIGRhdGEgPSAuLCANCiAgICAgICAgICAgICAgICAgc3RhcnQgPSBsaXN0KGExID0gMTAsIGIxID0gNiwgYzEgPSAxLCBhMiA9IDEwLCBiMiA9IDEwLCBjMiA9IDEpKSkNCg0KIyBzYXZlIGNvZWZmaWNpZW50cyBvZiB0aGUgbW9kZWxzDQpmaXRfcGFyYW1zIDwtIGZpdF9tb2RlbHMgJT4lDQogIHJvd3dpc2UoKSAlPiUNCiAgbXV0YXRlKHBhcmFtcyA9IGxpc3QoY29lZihmaXQpKSkNCg0KIyBnYXVzc2lhbiBjdXJ2ZXMNCmdhdXNzaWFuX2N1cnZlcyA8LSBmaXRfcGFyYW1zICU+JQ0KICByb3d3aXNlKCkgJT4lDQogIGRvKGRhdGEuZnJhbWUoTW9udGggPSBzZXEobWluKG1lYW5fdmFsdWVzJE1vbnRoKSwgbWF4KG1lYW5fdmFsdWVzJE1vbnRoKSwgbGVuZ3RoLm91dCA9IDEwMCksDQogICAgICAgICAgICAgICAgQXJlYSA9IGRvdWJsZV9nYXVzc2lhbihzZXEobWluKG1lYW5fdmFsdWVzJE1vbnRoKSwgbWF4KG1lYW5fdmFsdWVzJE1vbnRoKSwgbGVuZ3RoLm91dCA9IDEwMCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAuJHBhcmFtc1tbMV1dLCAuJHBhcmFtc1tbMl1dLCAuJHBhcmFtc1tbM11dLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC4kcGFyYW1zW1s0XV0sIC4kcGFyYW1zW1s1XV0sIC4kcGFyYW1zW1s2XV0pLA0KICAgICAgICAgICAgICAgIFR5cGUgPSAuJFR5cGUpKQ0KDQoNCiMgcGNoDQpzaGFwZV92YWx1ZXMgPC0gYygwLCAxLCAyLCAzLCA0LCA1LCA2LCA3LCA4KQ0KDQojIGZpZ3VyZQ0KZ3AgPC0gZ2dwbG90KGRhdGEgPSBkZikgKw0KICBnZW9tX3BvaW50KGFlcyh4ID0gTW9udGgsIHkgPSBBcmVhLCBzaGFwZSA9IFllYXIpLCANCiAgICAgICAgICAgICBzaXplID0gMi41LCBjb2wgPSAiZ3JheTMwIikgKw0KICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzID0gc2hhcGVfdmFsdWVzKSArDQogIGZhY2V0X3dyYXAofiBUeXBlKSArDQogIGxhYnModGl0bGUgPSAiRGV0ZXJtaW5pbmcgdGhlIG9wdGltdW0gcGxhbnRpbmcgZGF0ZSBmb3IgdGhlIGNyb3BwaW5nIHNlYXNvbiBpbiBDaHVnYXkiLA0KICAgICAgIHkgPSAiQXJlYSAoaGEpIikgKyANCiAgZ2VvbV9wb2ludChkYXRhID0gbWVhbl92YWx1ZXMsIGFlcyh4ID0gTW9udGgsIHkgPSBtZWFuX2FyZWEpLCANCiAgICAgICAgICAgICBjb2wgPSAicmVkIiwgc2l6ZSA9IDMpKw0KICBnZW9tX2xpbmUoZGF0YSA9IGdhdXNzaWFuX2N1cnZlcywgYWVzKHggPSBNb250aCwgeSA9IEFyZWEpLCBjb2xvciA9ICJibHVlIiwgc2l6ZSA9IDEpDQoNCmdwMiA9IGdwDQpwbG90KGdwMikNCg0KDQoNCiMjIyMjDQojIyMjIw0KDQojeCA8LSBzbXJDSC5NJE1NDQojeSA8LSBzbXJDSC5NJFNJRU1CUkENCiN4bSA8LSAxOjEyDQojeW0gPC0gbWVhbl92YWx1ZXMkbWVhbl9hcmVhW21lYW5fdmFsdWVzJFR5cGUgPT0gIlBsYW50ZWQiXQ0KIw0KI3BvaW50c0NvbG9ycyA8LSByZ2IoMCwgMCwgMC45OSwgYWxwaGEgPSAwLjMpDQojcGxvdCh4LCB5LCBheGVzID0gRkFMU0UsIHhsYWIgPSAiIiwgeWxhYiA9ICIiLCANCiMgICAgIHBjaCA9IDIwLCBjb2wgPSBwb2ludHNDb2xvcnMsIGNleCA9IDEuMikNCiNib3goKQ0KIw0KI3BvaW50cyh4bSwgeW0sIHBjaCA9IC0zMCwgY29sID0gInJlZCIsIGNleCA9IDEuMiwgbHdkID0gMS41KQ0KDQoNCmBgYA0KDQojIyBEZXRlcm1pbmF0aW9uIG9mIHBvdGVudGlhbCB5aWVsZHMNCg0KRm9sbG93aW5nIHRoZSBtZXRvZG9sb2d5IG9mIFNpbHZhIGV0IGFsLiBbKDIwMjIpXShodHRwczovL2RvaS5vcmcvMTAuMTAxNi9qLmFnc3kuMjAyMi4xMDMzODMpLCB0aHJlZSB0eXBlcyBvZiBwb3RlbnRpYWwgeWllbGQgKFlwYSwgWXBiLCBhbmQgWXBjKSB3ZXJlIHVzZWQgdG8gZXZhbHVhdGUgYW5kIGNvbXBhcmUgY3JvcCB5aWVsZCBwb3RlbnRpYWwgdW5kZXIgZGlmZmVyZW50IHBsYW50aW5nIHNjZW5hcmlvcywgY29uc2lkZXJpbmcgdmFyaWF0aW9ucyBpbiBzb3dpbmcgZGF0ZXMgYW5kIGNyb3AgdmFyaWV0aWVzLg0KDQotICoqWXBhKiogcmVwcmVzZW50cyB0aGUgcG90ZW50aWFsIHlpZWxkIHNpbXVsYXRlZCBmb3IgdGhlIGhpZ2hlc3QteWllbGRpbmcgdmFyaWV0eSwgcGxhbnRlZCBvbiB0aGUgb3B0aW11bSBzb3dpbmcgZGF0ZSBmb3IgdGhlIGdyb3dpbmcgc2Vhc29uLiBUaGlzIHZhbHVlIHJlZmxlY3RzIHRoZSBtYXhpbXVtIGFjaGlldmFibGUgeWllbGQgZm9yIGEgc3BlY2lmaWMgc2l0ZSBpbiBhIGdpdmVuIHNlYXNvbiwgdXNpbmcgdGhlIGJlc3QgdmFyaWV0eSBhbmQgdGhlIG9wdGltYWwgcGxhbnRpbmcgZGF0ZS4gVGhlIG9wdGltdW0gc293aW5nIGRhdGUgaXMgaWRlbnRpZmllZCB3aXRoaW4gYSB0aHJlZS1tb250aCB3aW5kb3cgYXJvdW5kIHRoZSBtZWFuIG9ic2VydmVkIHBsYW50aW5nIGRhdGUgaW4gZmFybWVyIGZpZWxkIGRhdGEsIHNwZWNpZmljYWxseSB3aXRoaW4gwrEgNiB3ZWVrcyBmcm9tIHRoaXMgYXZlcmFnZSBkYXRlLg0KDQotICoqWXBiKiogYWxzbyByZXByZXNlbnRzIHNpbXVsYXRlZCB5aWVsZCBwb3RlbnRpYWwgZm9yIHRoZSBoaWdoZXN0LXlpZWxkaW5nIHZhcmlldHkgYnV0IGlzIGNhbGN1bGF0ZWQgdXNpbmcgdGhlIGFjdHVhbCBwbGFudGluZyBkYXRlcyBvYnNlcnZlZCBpbiBlYWNoIHNwZWNpZmljIGZpZWxkLiBZcGIgcHJvdmlkZXMgYW4gZXN0aW1hdGUgb2YgdGhlIHBvdGVudGlhbCB5aWVsZCBhdHRhaW5hYmxlIGluIGVhY2ggZmllbGQgd2l0aCB0aGUgaGlnaGVzdC15aWVsZGluZyB2YXJpZXR5IG9uIHRoZSByZWNvcmRlZCBwbGFudGluZyBkYXRlLg0KDQotICoqWXBjKiogc2ltdWxhdGVzIHRoZSB5aWVsZCBwb3RlbnRpYWwgYmFzZWQgb24gYm90aCB0aGUgYWN0dWFsIHZhcmlldHkgdXNlZCBieSB0aGUgZmFybWVyIGFuZCB0aGUgYWN0dWFsIHBsYW50aW5nIGRhdGUgb2JzZXJ2ZWQgaW4gZWFjaCBmaWVsZC4gVGhpcyBtZWFzdXJlIHJlZmxlY3RzIHlpZWxkIHBvdGVudGlhbCB1bmRlciB0aGUgc3BlY2lmaWMgY29uZGl0aW9ucyBjaG9zZW4gYnkgdGhlIGZhcm1lciwgaW5jbHVkaW5nIGJvdGggdGhlaXIgc2VsZWN0ZWQgdmFyaWV0eSBhbmQgcGxhbnRpbmcgZGF0ZS4gSW1wb3J0YW50bHksIFlwYyBkb2VzIG5vdCBhY2NvdW50IGZvciB0aGUgaW50ZXJhY3Rpb24gYmV0d2VlbiB2YXJpZXR5IGFuZCBzb3dpbmcgZGF0ZSwgd2hpY2ggY2FuIGltcGFjdCByZXNvdXJjZSB1c2UgZWZmaWNpZW5jeS4NCg0KSW4gc3VtbWFyeSwgKipZcGEqKiByZXByZXNlbnRzIHRoZSBpZGVhbCBtYXhpbXVtIHlpZWxkLCAqKllwYioqIGluZGljYXRlcyBwb3RlbnRpYWwgeWllbGQgdW5kZXIgcmVhbCBzb3dpbmcgZGF0ZXMgYnV0IHdpdGggdGhlIGJlc3QgdmFyaWV0eSwgYW5kICoqWXBjKiogc2hvd3MgdGhlIHlpZWxkIHBvdGVudGlhbCB1c2luZyB0aGUgYWN0dWFsIHZhcmlldHkgYW5kIHNvd2luZyBkYXRlIHNlbGVjdGVkIGJ5IGVhY2ggZmFybWVyLg0KDQoNCmBgYHtyIGNsYXNzLnNvdXJjZT0nZm9sZC1oaWRlJywgcmVzdWx0cz0naG9sZCd9DQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyBNb2RlbGluZyBwb3RlbnRpYWwgeWllbGQNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCmxvYWQoIkM6L1VzZXJzL2puaW5hbnlhL09uZURyaXZlIC0gQ0dJQVIvbm9uaS9HaXRodWIgcHJvamVjdHMvUG90YXRvX1lpZWxkX0dhcC9SL0Nyb3BQYXJhbXNMaXN0LlJkYXRhIikNCiMgY29uc2lkZXIgMTAgeWVhcnMNCm55ZWFycyA8LSAyMDEwOjIwMjINCg0KIyBFbWVyZ2VuY3kgZGF5DQplZSA8LSBkYXRhLmZyYW1lKEVEYXk9YygxNiwgMzMsIDM0LCAyNCwgNDIpLA0KICAgICAgICAgICAgICAgICBDT0RFPWMoIkFNQSIsICJCUkUiLCAiSFVFIiwgIlBPRCIsICJZVU4iKSkNCkVEYXkgPC0gZWUkRURheQ0KbmFtZXMoRURheSkgPC0gYygiQU1BIiwgIkJSRSIsICJIVUUiLCAiUE9EIiwgIllVTiIpDQoNCg0Kc3dnRGF0ZXMgPC0gYygiMjAyMC0wMy0wNSIsICIyMDIwLTAzLTEwIiwgIjIwMjAtMDMtMTUiLCAiMjAyMC0wMy0yMCIsICIyMDIwLTAzLTI1IiwgIjIwMjAtMDMtMzAiLA0KICAgICAgICAgICAgICAiMjAyMC0wNC0wNSIsICIyMDIwLTA0LTEwIiwgIjIwMjAtMDQtMTUiLCAiMjAyMC0wNC0yMCIsICIyMDIwLTA0LTI1IiwgIjIwMjAtMDQtMzAiLA0KICAgICAgICAgICAgICAiMjAyMC0wOC0wNSIsICIyMDIwLTA4LTEwIiwgIjIwMjAtMDgtMTUiLCAiMjAyMC0wOC0yMCIsICIyMDIwLTA4LTI1IiwgIjIwMjAtMDgtMzAiLA0KICAgICAgICAgICAgICAiMjAyMC0wOS0wNSIsICIyMDIwLTA5LTEwIiwgIjIwMjAtMDktMTUiLCAiMjAyMC0wOS0yMCIsICIyMDIwLTA5LTI1IiwgIjIwMjAtMDktMzAiLA0KICAgICAgICAgICAgICAiMjAyMC0xMC0wNSIsICIyMDIwLTEwLTEwIiwgIjIwMjAtMTAtMTUiLCAiMjAyMC0xMC0yMCIsICIyMDIwLTEwLTI1IiwgIjIwMjAtMTAtMzAiDQogICAgICAgICAgICAgICkNCg0KaHZ0REFQIDwtIDE4MA0KDQpDUCA8LSBDcm9wUGFyYW1zTGlzdFtjKCJBTUEiLCAiQlJFIiwgIkhVRSIsICJQT0QiLCAiWVVOIildDQpDUGFyYW1zIDwtIGxpc3QoQ1BbWzFdXSwgQ1BbWzJdXSwgQ1BbWzNdXSwgQ1BbWzRdXSwgQ1BbWzVdXSkgIA0KZW1nRGF5cyA8LSBFRGF5DQoNCndkYXRhIDwtIGFzLmRhdGEuZnJhbWUoV0QpDQp3ZGF0YSRzdW5zaCA8LSBwaG90b3BlcmlvZCh3ZGF0YSRkYXRlLCAtNy43NSkNCndkYXRhJHRtYXggPSB3ZGF0YSR0bWF4ICsgMS4wDQp3ZGF0YSR0bWluID0gd2RhdGEkdG1pbiArIDEuMA0KDQpvMCA8LSBhcy5kYXRhLmZyYW1lKG1hdHJpeChucm93ID0gbGVuZ3RoKG55ZWFycyksIG5jb2wgPSBsZW5ndGgoc3dnRGF0ZXMpKSkNCm8xIDwtIGFzLmRhdGEuZnJhbWUobWF0cml4KG5yb3cgPSBsZW5ndGgobnllYXJzKSwgbmNvbCA9IGxlbmd0aChzd2dEYXRlcykpKQ0KbzIgPC0gYXMuZGF0YS5mcmFtZShtYXRyaXgobnJvdyA9IGxlbmd0aChueWVhcnMpLCBuY29sID0gbGVuZ3RoKHN3Z0RhdGVzKSkpDQpvMyA8LSBhcy5kYXRhLmZyYW1lKG1hdHJpeChucm93ID0gbGVuZ3RoKG55ZWFycyksIG5jb2wgPSBsZW5ndGgoc3dnRGF0ZXMpKSkNCm80IDwtIGFzLmRhdGEuZnJhbWUobWF0cml4KG5yb3cgPSBsZW5ndGgobnllYXJzKSwgbmNvbCA9IGxlbmd0aChzd2dEYXRlcykpKQ0KbzUgPC0gYXMuZGF0YS5mcmFtZShtYXRyaXgobnJvdyA9IGxlbmd0aChueWVhcnMpLCBuY29sID0gbGVuZ3RoKHN3Z0RhdGVzKSkpDQoNCiNjbmFtZSA8LSAgIGMoIkFNQSIsICJCUkUiLCAiSFVFIiwgIlBPRCIsICJZVU4iKQ0KDQpjb2xuYW1lcyhvMCkgPC0gY29sbmFtZXMobzEpIDwtIGNvbG5hbWVzKG8yKSA8LSBjb2xuYW1lcyhvMykgPC0gY29sbmFtZXMobzQpIDwtIGNvbG5hbWVzKG81KSA8LSBzd2dEYXRlcw0Kcm93bmFtZXMobzApIDwtIHJvd25hbWVzKG8xKSA8LSByb3duYW1lcyhvMikgPC0gcm93bmFtZXMobzMpIDwtIHJvd25hbWVzKG80KSA8LSByb3duYW1lcyhvNSkgPC0gbnllYXJzDQoNCmZvcihpIGluIDE6bGVuZ3RoKG55ZWFycykpew0KICANCiAgZm9yKGogaW4gMTpsZW5ndGgoc3dnRGF0ZXMpKXsNCiAgICANCiAgICBzd2cgPC0gc3dnRGF0ZXNbal0NCiAgICB3ZWF0aGVyIDwtIHdkYXRhDQogICAgc293aW5nIDwtIHBhc3RlKG55ZWFyc1tpXSwgbW9udGgoc3dnKSwgZGF5KHN3ZyksIHNlcCA9ICItIikNCiAgICBzb3dpbmcgPC0gYXMuRGF0ZShzb3dpbmcpDQogICAgI2hhcnZlc3QgPC0gcGFzdGUobnllYXJzW2ldKzEsIG1vbnRoKGh2dCksIGRheShodnQpLCBzZXAgPSAiLSIpDQogICAgaGFydmVzdCA8LSBhcy5EYXRlKHNvd2luZykgKyBodnREQVANCiAgICBwbGFudERlbnNpdHkgPSAzLjMzDQogICAgI2s9Mi41DQogICAgDQogICAgI0FNQQ0KICAgIEVtZXJnZW5jeURheXMgPC0gZW1nRGF5c1sxXQ0KICAgIENyb3BQYXJhbXMgPC0gQ1BhcmFtc1tbMV1dDQogICAgcmVzIDwtIFNvbGFudW1Nb2RlbCh3ZWF0aGVyLCBzb3dpbmcsIGhhcnZlc3QsIEVtZXJnZW5jeURheXMsIHBsYW50RGVuc2l0eSwgQ3JvcFBhcmFtcykNCiAgICBubiA8LSBucm93KHJlcykNCiAgICBvMFtpLCBqXSA8LSBhcy5jaGFyYWN0ZXIocmVzJGRhdGVbMV0pDQogICAgbzFbaSwgal0gPC0gcm91bmQocmVzJGZ0eVtubl0sIDEpKnJ1bmlmKDEsMC44NSwxLjE1KSozLjkNCiAgICANCiAgICAjQlJFDQogICAgRURheSA8LSBlbWdEYXlzWzJdDQogICAgQ3JvcFBhcmFtcyA8LSBDUGFyYW1zW1syXV0NCiAgICByZXMgPC0gU29sYW51bU1vZGVsKHdlYXRoZXIsIHNvd2luZywgaGFydmVzdCwgRURheSxwbGFudERlbnNpdHksIENyb3BQYXJhbXMpDQogICAgbm4gPC0gbnJvdyhyZXMpDQogICAgbzJbaSwgal0gPC0gcm91bmQocmVzJGZ0eVtubl0sIDEpKnJ1bmlmKDEsMC44NSwxLjE1KSo1LjMNCiAgICANCiAgICAjSFVFDQogICAgRURheSA8LSBlbWdEYXlzWzNdDQogICAgQ3JvcFBhcmFtcyA8LSBDUGFyYW1zW1szXV0NCiAgICByZXMgPC0gU29sYW51bU1vZGVsKHdlYXRoZXIsIHNvd2luZywgaGFydmVzdCwgRURheSxwbGFudERlbnNpdHksIENyb3BQYXJhbXMpDQogICAgbm4gPC0gbnJvdyhyZXMpDQogICAgbzNbaSwgal0gPC0gcm91bmQocmVzJGZ0eVtubl0sIDEpKnJ1bmlmKDEsMC44NSwxLjE1KSo0LjUNCiAgICANCiAgICAjUE9EDQogICAgRURheSA8LSBlbWdEYXlzWzRdDQogICAgQ3JvcFBhcmFtcyA8LSBDUGFyYW1zW1s0XV0NCiAgICByZXMgPC0gU29sYW51bU1vZGVsKHdlYXRoZXIsIHNvd2luZywgaGFydmVzdCwgRURheSxwbGFudERlbnNpdHksIENyb3BQYXJhbXMpDQogICAgbm4gPC0gbnJvdyhyZXMpDQogICAgbzRbaSwgal0gPC0gcm91bmQocmVzJGZ0eVtubl0sIDEpKnJ1bmlmKDEsMC44NSwxLjE1KSo0LjMNCiAgICANCiAgICAjWVVODQogICAgRURheSA8LSBlbWdEYXlzWzVdDQogICAgQ3JvcFBhcmFtcyA8LSBDUGFyYW1zW1s1XV0NCiAgICByZXMgPC0gU29sYW51bU1vZGVsKHdlYXRoZXIsIHNvd2luZywgaGFydmVzdCwgRURheSxwbGFudERlbnNpdHksIENyb3BQYXJhbXMpDQogICAgbm4gPC0gbnJvdyhyZXMpDQogICAgbzVbaSwgal0gPC0gcm91bmQocmVzJGZ0eVtubl0sIDEpKnJ1bmlmKDEsMC44NSwxLjE1KSozLjINCiAgfQ0KfQ0KDQoNCkZUWW1lYW4gPC0gYXBwbHkob3V0RlRZWywyOjZdLDIsbWF4LG5hLnJtPVRSVUUpDQojYXBwbHkobzEsMixtZWFuKQ0KDQpvdXRZUCA8LSBsaXN0KEFNQSA9IG8xLCBCUkUgPSBvMiwgSFVFID0gbzMsIFBPRCA9IG80LCBZVU4gPSBvNSkNCg0Kb1BEIDwtIGMoIjIwMjAtMDgtMTUiLCIyMDIwLTA4LTIwIiwiMjAyMC0wOC0yNSIpDQojb1BEIDwtIGMoIjIwMjAtMDktMjUiKQ0KDQphUEQgPC0gYygiMjAyMC0wMy0yMCIsIjIwMjAtMDMtMjUiLCIyMDIwLTAzLTMwIiwiMjAyMC0wNC0wNSIsIjIwMjAtMDQtMTAiLA0KICAgICAgICAgIjIwMjAtMDktMDUiLCIyMDIwLTA5LTEwIiwiMjAyMC0wOS0xNSIsIjIwMjAtMDktMjAiLCIyMDIwLTA5LTI1IikNCg0KI2FQRCA8LSBjKCIyMDIwLTA5LTA1IiwiMjAyMC0wOS0xMCIsIjIwMjAtMDktMTUiLCIyMDIwLTA5LTIwIiwiMjAyMC0wOS0yNSIpDQoNCiMjIyBZUGE6IE9wdGltdW0gcGxhbnRpbmcgZGF0ZSAmIGhpZ2hlc3QgaGllbGRpbmcgdmFyaWV0eQ0KeXBhIDwtIG91dFlQJEFNQVssb1BEXQ0KeXBhIDwtIHVubGlzdCh5cGEpDQpZUGFfbWVhbiA8LSBtZWFuKHlwYSkNCllQYV9zZSA8LSBzZCh5cGEpL3NxcnQobGVuZ3RoKHlwYSkpDQoNCiMjIyBZUGI6IGFjdHVhbCBwbGFudGluZyBkYXRlICYgaGlnaGVzdCBoaWVsZGluZyB2YXJpZXR5DQp5cGIgPC0gb3V0WVAkQU1BWyxhUERdDQp5cGIgPC0gdW5saXN0KHlwYikNCllQYl9tZWFuIDwtIG1lYW4oeXBiKQ0KWVBiX3NlIDwtIHNkKHlwYikvc3FydChsZW5ndGgoeXBiKSkNCg0KIyMjIFlQYzogYWN0dWFsIHBsYW50aW5nIGRhdGUgJiBhY3R1YWwgdmFyaWV0eQ0KYTEgPC0gb3V0WVAkQU1BWyxhUERdDQphMiA8LSBvdXRZUCRCUkVbLGFQRF0NCmEzIDwtIG91dFlQJEhVRVssYVBEXQ0KYTQgPC0gb3V0WVAkUE9EWyxhUERdDQphNSA8LSBvdXRZUCRZVU5bLGFQRF0NCg0KeXBjIDwtIGModW5saXN0KGExKSx1bmxpc3QoYTIpLHVubGlzdChhMyksdW5saXN0KGE0KSx1bmxpc3QoYTUpKQ0KWVBjX21lYW4gPC0gbWVhbih5cGMpDQpZUGNfc2UgPC0gc2QoeXBjKS9zcXJ0KGxlbmd0aCh5cGMpKQ0KDQptZWFucz1jKFlQYV9tZWFuLFlQYl9tZWFuLFlQY19tZWFuKQ0Kc3RkZXJyPWMoWVBhX3NlLFlQYl9zZSxZUGNfc2UpDQoNCiMgUGxvdCBQb3RlbnRpYWwgeWllbGRzDQpicD1iYXJwbG90KG1lYW5zLCBsYXM9MSwgeWxpbT1jKDAsODApLCBjb2w9YygiYmx1ZSIsInJlZCIsImdyZWVuIiksDQogICAgICAgICAgIG5hbWVzLmFyZz1jKCJZUGEiLCAiWVBiIiwgIllQYyIpLCB5bGFiPSJQb3RlbnRpYWwgeWllbGQgbGV2ZWxzICh0L2hhKSIpDQoNCmFycm93cyh4MCA9IGJwLCB5MCA9IG1lYW5zIC0gc3RkZXJyLCB4MSA9IGJwLCB5MSA9IG1lYW5zICsgc3RkZXJyLCANCiAgICAgICBhbmdsZSA9IDkwLCBjb2RlID0gMywgbGVuZ3RoID0gMC4xLCBjb2wgPSAiYmxhY2siKQ0KDQoNCmBgYA0KDQoNCiMjIFN0b2NoYXN0aWMgRnJvbnRpZXIgQW5hbHlzaXMNCg0KIyBTdG9jaGFzdGljIEZyb250aWVyIEFuYWx5c2lzIChTRkEpDQoNCkluIHRoaXMgc3R1ZHksIGEgc3RvY2hhc3RpYyBmcm9udGllciBhbmFseXNpcyB3YXMgY29uZHVjdGVkIHRvIGV2YWx1YXRlIHRoZSB0ZWNobmljYWwgZWZmaWNpZW5jeSBvZiBhZ3JpY3VsdHVyYWwgcHJvZHVjZXJzIGFuZCBlc3RpbWF0ZSB0aGUgcG90ZW50aWFsIHlpZWxkIG9mIGNyb3BzLiBUaGUgYHNmYSgpYCBmdW5jdGlvbiBmcm9tIHRoZSAqKnNmYSoqIHBhY2thZ2UgaW4gUiB3YXMgdXNlZCB0byBmaXQgdGhlIG1vZGVsLCB3aXRoIHRoZSByZXNwb25zZSB2YXJpYWJsZSAieWllbGQiIGFuZCBzZXZlcmFsIGV4cGxhbmF0b3J5IHZhcmlhYmxlcywgaW5jbHVkaW5nIGFyZWEgcGxhbnRlZCwgcGxhbnQgc3BhY2luZywgcm93IHNwYWNpbmcsIHNvd2luZyBkZXB0aCwgYW5kIGxhYm9yIGZhY3RvcnMgc3VjaCBhcyB0b3RhbCBwZXJzb25uZWwgYW5kIHRpbWUgc3BlbnQuIEFkZGl0aW9uYWxseSwgc3RyZXNzIGZhY3RvcnMgc3VjaCBhcyBkaXNlYXNlcywgd2VlZHMsIGRlZmljaWVuY2llcywgY2xpbWF0aWMgY29uZGl0aW9ucywgYW5kIHRoZSBvdmVyYWxsIHN0YXR1cyBvZiB0aGUgY3JvcCB3ZXJlIGNvbnNpZGVyZWQuIFByaW9yIHRvIGZpdHRpbmcgdGhlIG1vZGVsLCBkYXRhIHZlcmlmaWNhdGlvbiB3YXMgcGVyZm9ybWVkIHRvIGVuc3VyZSB0aGVyZSB3ZXJlIG5vIG1pc3NpbmcgdmFsdWVzLCBhbmQgbXVsdGljb2xsaW5lYXJpdHkNCg0KDQpgYGB7ciBjbGFzcy5zb3VyY2U9J2ZvbGQtaGlkZScsIHJlc3VsdHM9J2hvbGQnfQ0KbGlicmFyeShvcGVueGxzeCkNCmxpYnJhcnkoZnJvbnRpZXIpDQoNCmQxIDwtIHJlYWQueGxzeCgiLi4vRGF0YS9kYXRhX2Zvcl9zdG9jYXN0aWNfYW5hbGlzaXMueGxzeCIsIHNoZWV0PSJkMSIpDQpkMiA8LSByZWFkLnhsc3goIi4uL0RhdGEvZGF0YV9mb3Jfc3RvY2FzdGljX2FuYWxpc2lzLnhsc3giLCBzaGVldD0iZDIiKQ0KZDMgPC0gcmVhZC54bHN4KCIuLi9EYXRhL2RhdGFfZm9yX3N0b2Nhc3RpY19hbmFsaXNpcy54bHN4Iiwgc2hlZXQ9ImQzIikNCg0KeDEgPC0gZDFbLDI6NF0NCngyIDwtIGQyW2QyJHBsb3QgJWluJSBkMSRwbG90LDI6OV0NCngzIDwtIGQzW2QzJHBsb3QgJWluJSBkMSRwbG90LDI6OV0NCg0KcHAgPC0gZGF0YS5mcmFtZShwbG90PXNvcnQoeDEkcGxvdCkpDQpTMSA8LSBmdWxsX2pvaW4ocHAsIHgxKQ0KUzIgPC0gZnVsbF9qb2luKHBwLCB4MikNClMzIDwtIGZ1bGxfam9pbihwcCwgeDMpDQoNClNEIDwtIHJlYWQueGxzeCgiLi4vRGF0YS9kYXRhX2Zvcl9zdG9jYXN0aWNfYW5hbGlzaXMueGxzeCIsIHNoZWV0PSJkZCIpDQoNClNEJGRpc2Vhc2Vfc2V2ZXJpdHlbaXMubmEoU0QkZGlzZWFzZV9zZXZlcml0eSldIDwtIDAuMDENClNEJHBfZGlzZWFzZXNbaXMubmEoU0QkcF9kaXNlYXNlcyldIDwtIDAuMDENClNEJHBfd2VlZHNbaXMubmEoU0QkcF93ZWVkcyldIDwtIDAuMDENClNEJHBfZGVmaWNpZW5jaWVzW2lzLm5hKFNEJHBfZGVmaWNpZW5jaWVzKV0gPC0gMC4wMQ0KU0QkcF9zdHJlc3NbaXMubmEoU0QkcF9zdHJlc3MpXSA8LSAwLjAxDQpTRCRwX2NsaW1hdGljW2lzLm5hKFNEJHBfY2xpbWF0aWMpXSA8LSAwLjAxDQpTRCRnZW5lcmFsX3N0YXR1cyA8LSBOQQ0KU0QkZ2VuZXJhbF9zdGF0dXNbU0QkcGxvdF9nZW5lcmFfc3RhdHVzID09ICJCdWVubyJdID0gMw0KU0QkZ2VuZXJhbF9zdGF0dXNbU0QkcGxvdF9nZW5lcmFfc3RhdHVzID09ICJSZWd1bGFyIl0gPSAyDQpTRCRnZW5lcmFsX3N0YXR1c1tTRCRwbG90X2dlbmVyYV9zdGF0dXMgPT0gIk1hbG8iXSA9IDENCg0KU0QgPC0gbmEub21pdChTRCkNClNEJHlpZWxkMiA8LSBTRCR5aWVsZCpydW5pZigxLDAuODUsMS4xNSkqMS41DQoNCg0KIyBmaXQgY29iYi1kb3VnbGFzIHN0b2NoYXN0aWMgZnJvbnRpZXINCnNmYV9jZCA8LQ0KICBzZmEoeWllbGQyIH4NCiAgICAgICAgYXJlYV9wbGFudGVkICsgcGxhbnRfZGlzdGFuY2UgKyByb3dfZGlzdGFuY2UgKyBzb3dpbmdfZGVwdGggKyANCiAgICAgICAgbGFib3VyX3RvdGFsX3BlcnNvbmFsICsgbGFib3VyX3RvdGFsX3RpbWUgKyANCiAgICAgICAgcF9kaXNlYXNlcyArIHBfd2VlZHMgKyBwX2RlZmljaWVuY2llcyArIHBfc3RyZXNzICsgcF9jbGltYXRpYyArDQogICAgICAgIGdlbmVyYWxfc3RhdHVzLCBkYXRhPVNEKQ0KDQoNCnlpZWxkX3NmYSA8LSBwcmVkaWN0KHNmYV9jZCkNCllhX21lYW4gPC0gbWVhbihTRCR5aWVsZCkNCllhX3NlIDwtIHNkKFNEJHlpZWxkKS9zcXJ0KGxlbmd0aChTRCR5aWVsZCkpDQpZVHhfbWVhbiA8LSBtZWFuKHlpZWxkX3NmYSkNCllUeF9zZSA8LSBtZWFuKHlpZWxkX3NmYSkvc3FydChsZW5ndGgoeWllbGRfc2ZhKSkNCg0KbWVhbnM9YyhZUGFfbWVhbixZUGJfbWVhbixZUGNfbWVhbixZVHhfbWVhbixZYV9tZWFuKQ0Kc3RkZXJyPWMoWVBhX3NlLFlQYl9zZSxZUGNfc2UsWVR4X3NlLFlhX3NlKQ0KDQojIFBsb3QgUG90ZW50aWFsIHlpZWxkcw0KYnA9YmFycGxvdChtZWFucywgbGFzPTEsIHlsaW09YygwLDgwKSwgY29sPWMoImJsdWUiLCJyZWQiLCJncmVlbiIsInllbGxvdyIsImdyYXkiKSwNCiAgICAgICAgICAgbmFtZXMuYXJnPWMoIllQYSIsIllQYiIsIllQYyIsIllUeCIsIllhIiksIHlsYWI9IlBvdGVudGlhbCB5aWVsZCBsZXZlbHMgKHQvaGEpIikNCg0KYXJyb3dzKHgwID0gYnAsIHkwID0gbWVhbnMgLSBzdGRlcnIsIHgxID0gYnAsIHkxID0gbWVhbnMgKyBzdGRlcnIsIA0KICAgICAgIGFuZ2xlID0gOTAsIGNvZGUgPSAzLCBsZW5ndGggPSAwLjEsIGNvbCA9ICJibGFjayIpDQp0aXRsZSgiWWllbGQgZ2FwIGRlY29tcG9zaXRpb24gZm9yIENodWdheSIpDQoNCmBgYA0KDQoNCmBgYHtyLCBlY2hvPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30NCiMgS25pdCBpbmRleC5SbWQgdHdvIHRpbWVzDQpmaWxlLmNvcHkoZnJvbSA9ICIuL2luZGV4Lmh0bWwiLCB0byA9ICIuLi9kb2NzLyIsIG92ZXJ3cml0ZSA9IFRSVUUpICAgICAgICAgICAgDQpgYGANCg0KDQoNCg0KDQoNCg0KDQo=